<commit_before>package consensus

import (
	"errors"
	"fmt"
	"log"
	"sync"
	"time"

	. "github.com/divtxt/raft"
	"github.com/divtxt/raft/config"
	"github.com/divtxt/raft/consensus/candidate"
	"github.com/divtxt/raft/consensus/leader"
	"github.com/divtxt/raft/internal"
	"github.com/divtxt/raft/logindex"
	"github.com/divtxt/raft/util"
)

type PassiveConsensusModule struct {
	lock *sync.Mutex

	// ===== the following fields meant to be immutable

	// -- External components
	RaftPersistentState         RaftPersistentState
	logRO                       internal.LogTailOnlyRO
	logWO                       internal.LogTailOnlyWO
	sendOnlyRpcRequestVoteAsync internal.SendOnlyRpcRequestVoteAsync
	aeSender                    internal.IAppendEntriesSender
	logger                      *log.Logger

	// -- Config
	ClusterInfo *config.ClusterInfo

	// ===== the following fields are mutable

	// -- State - for all servers
	serverState ServerState

	// commitIndex is the index of highest log entry known to be committed
	// (initialized to 0, increases monotonically)
	commitIndex            *logindex.WatchedIndex
	electionTimeoutChooser *util.ElectionTimeoutChooser
	ElectionTimeoutTimer   *util.Timer

	// -- State - for candidates only
	CandidateVolatileState *candidate.CandidateVolatileState

	// -- State - for leaders only
	LeaderVolatileState *leader.LeaderVolatileState
}

func NewPassiveConsensusModule(
	raftPersistentState RaftPersistentState,
	log internal.LogTailOnly,
	sendOnlyRpcRequestVoteAsync internal.SendOnlyRpcRequestVoteAsync,
	aeSender internal.IAppendEntriesSender,
	clusterInfo *config.ClusterInfo,
	electionTimeoutLow time.Duration,
	nowFunc func() time.Time,
	logger *log.Logger,
) (*PassiveConsensusModule, error) {
	// Param checks
	if raftPersistentState == nil {
		return nil, errors.New("'raftPersistentState' cannot be nil")
	}
	if log == nil {
		return nil, errors.New("'log' cannot be nil")
	}
	if sendOnlyRpcRequestVoteAsync == nil {
		return nil, errors.New("'sendOnlyRpcRequestVoteAsync' cannot be nil")
	}
	if clusterInfo == nil {
		return nil, errors.New("clusterInfo cannot be nil")
	}
	if electionTimeoutLow.Nanoseconds() <= 0 {
		return nil, errors.New("electionTimeoutLow must be greater than zero")
	}

	lock := &sync.Mutex{}
	electionTimeoutTimer := util.NewTimer(electionTimeoutLow, nowFunc)

	pcm := &PassiveConsensusModule{
		lock,

		// -- External components
		raftPersistentState,
		log,
		log,
		sendOnlyRpcRequestVoteAsync,
		aeSender,
		logger,

		// -- Config
		clusterInfo,

		// -- State - for all servers
		// #5.2-p1s2: When servers start up, they begin as followers
		FOLLOWER,

		// -- State - for all servers
		// commitIndex is the index of highest log entry known to be committed
		// (initialized to 0, increases monotonically)
		logindex.NewWatchedIndex(lock),
		util.NewElectionTimeoutChooser(electionTimeoutLow),
		electionTimeoutTimer,

		// -- State - for candidates only
		nil,

		// -- State - for leaders only
		nil,
	}

	return pcm, nil
}

// Get the current server state.
// Validates the server state before returning.
func (cm *PassiveConsensusModule) GetServerState() ServerState {
	cm.lock.Lock()
	defer cm.lock.Unlock()

	return cm.serverState
}

// Set the current server state.
// Validates the server state before setting.
func (cm *PassiveConsensusModule) setServerState(serverState ServerState) {
	if serverState != FOLLOWER && serverState != CANDIDATE && serverState != LEADER {
		panic(fmt.Sprintf("FATAL: unknown ServerState: %v", serverState))
	}
	if cm.serverState != serverState {
		cm.logger.Println(
			"[raft] setServerState:",
			ServerStateToString(cm.serverState),
			"->",
			ServerStateToString(serverState),
		)
		cm.serverState = serverState
	}
}

// Get the current commitIndex value.
func (cm *PassiveConsensusModule) GetCommitIndex() LogIndex {
	cm.lock.Lock()
	defer cm.lock.Unlock()

	return cm.commitIndex.UnsafeGet()
}

// Get the commitIndex WatchableIndex.
func (cm *PassiveConsensusModule) GetCommitIndexWatchable() WatchableIndex {
	// No need to lock since this is an immutable field.
	return cm.commitIndex
}

// Set the current commitIndex value.
// Checks that it is does not reduce.
func (cm *PassiveConsensusModule) setCommitIndex(commitIndex LogIndex) error {
	if commitIndex < cm.commitIndex.UnsafeGet() {
		return fmt.Errorf(
			"setCommitIndex to %v < current commitIndex %v",
			commitIndex,
			cm.commitIndex.UnsafeGet(),
		)
	}
	// FIXME: check against lastCompacted as well!
	iole := cm.logRO.GetIndexOfLastEntry()
	if commitIndex > iole {
		return fmt.Errorf(
			"setCommitIndex to %v > current indexOfLastEntry %v",
			commitIndex,
			iole,
		)
	}
	err := cm.commitIndex.UnsafeSet(commitIndex)
	return err
}

// AppendCommand appends the given serialized command to the Raft log and returns
// the index of the appended entry.
func (cm *PassiveConsensusModule) AppendCommand(command Command) (LogIndex, error) {
	cm.lock.Lock()
	defer cm.lock.Unlock()

	if cm.serverState != LEADER {
		return 0, ErrNotLeader
	}

	termNo := cm.RaftPersistentState.GetCurrentTerm()
	logEntry := LogEntry{termNo, command}
	return cm.logWO.AppendEntry(logEntry)
}

// Iterate
func (cm *PassiveConsensusModule) Tick() error {
	cm.lock.Lock()
	defer cm.lock.Unlock()

	switch cm.serverState {
	case FOLLOWER:
		// #RFS-F2: If election timeout elapses without receiving
		// AppendEntries RPC from current leader or granting vote
		// to candidate: convert to candidate
		// #5.2-p1s5: If a follower receives no communication over a period
		// of time called the election timeout, then it assumes there is no
		// viable leader and begins an election to choose a new leader.
		fallthrough
	case CANDIDATE:
		// #RFS-C4: If election timeout elapses: start new election
		// #5.2-p5s1: The third possible outcome is that a candidate neither
		// wins nor loses the election; ... votes could be split so that no
		// candidate obtains a majority.
		// #5.2-p5s2: When this happens, each candidate will time out and
		// start a new election by incrementing its term and initiating
		// another round of RequestVote RPCs.
		if cm.ElectionTimeoutTimer.Expired() {
			cm.logger.Println("[raft] Election timeout - starting a new election")
			err := cm.becomeCandidateAndBeginElection()
			if err != nil {
				return err
			}
			// *** SOLO ***
			// Single node cluster wins election immediately since it has all the votes
			// But don't skip the election process, mainly since it increases current term!
			if cm.ClusterInfo.GetClusterSize() == 1 {
				cm.logger.Println("[raft] Single node cluster - win election immediately!")
				err := cm.becomeLeader()
				if err != nil {
					return err
				}
			}
		}
	case LEADER:
		// #RFS-L4: If there exists an N such that N > commitIndex, a majority
		// of matchIndex[i] >= N, and log[N].term == currentTerm:
		// set commitIndex = N (#5.3, #5.4)
		err := cm.advanceCommitIndexIfPossible()
		if err != nil {
			return err
		}
		// #RFS-L3.0: If last log index >= nextIndex for a follower: send
		// AppendEntries RPC with log entries starting at nextIndex
		err = cm.sendAppendEntriesToAllPeers(false)
		if err != nil {
			return err
		}
	}
	return nil
}

func (cm *PassiveConsensusModule) becomeCandidateAndBeginElection() error {
	// #RFS-C1: On conversion to candidate, start election:
	// Increment currentTerm; Vote for self; Send RequestVote RPCs
	// to all other servers; Reset election timer
	// #5.2-p2s1: To begin an election, a follower increments its
	// current term and transitions to candidate state.
	newTerm := cm.RaftPersistentState.GetCurrentTerm() + 1
	err := cm.RaftPersistentState.SetCurrentTerm(newTerm)
	if err != nil {
		return err
	}
	cm.CandidateVolatileState, err = candidate.NewCandidateVolatileState(cm.ClusterInfo)
	if err != nil {
		return err
	}
	// FIXME: return newTerm to avoid logging here
	cm.logger.Println("[raft] becomeCandidateAndBeginElection: newTerm =", newTerm)
	cm.setServerState(CANDIDATE)
	// #5.2-p2s2: It then votes for itself and issues RequestVote RPCs
	// in parallel to each of the other servers in the cluster.
	err = cm.RaftPersistentState.SetVotedFor(cm.ClusterInfo.GetThisServerId())
	if err != nil {
		return err
	}
	lastLogIndex, lastLogTerm, err := GetIndexAndTermOfLastEntry(cm.logRO)
	if err != nil {
		return err
	}
	err = cm.ClusterInfo.ForEachPeer(
		func(serverId ServerId) error {
			rpcRequestVote := &RpcRequestVote{newTerm, lastLogIndex, lastLogTerm}
			cm.sendOnlyRpcRequestVoteAsync(serverId, rpcRequestVote)
			return nil
		},
	)
	if err != nil {
		return err
	}
	// Reset election timeout!
	cm.ElectionTimeoutTimer.RestartWithDuration(
		cm.electionTimeoutChooser.ChooseRandomElectionTimeout(),
	)
	return nil
}

func (cm *PassiveConsensusModule) becomeLeader() error {
	iole := cm.logRO.GetIndexOfLastEntry()
	var err error
	cm.LeaderVolatileState, err = leader.NewLeaderVolatileState(cm.ClusterInfo, iole, cm.aeSender)
	if err != nil {
		return err
	}
	cm.logger.Println(
		"[raft] becomeLeader: iole =", iole, ", commitIndex =", cm.commitIndex.UnsafeGet(),
	)
	cm.setServerState(LEADER)
	// #RFS-L1a: Upon election: send initial empty AppendEntries RPCs (heartbeat)
	// to each server;
	err = cm.sendAppendEntriesToAllPeers(true)
	if err != nil {
		return err
	}
	return nil
}

func (cm *PassiveConsensusModule) becomeFollowerWithTerm(newTerm TermNo) error {
	currentTerm := cm.RaftPersistentState.GetCurrentTerm()
	if cm.serverState == FOLLOWER && currentTerm == newTerm {
		// Nothing to change!
		return nil
	}
	cm.logger.Println("[raft] becomeFollowerWithTerm: newTerm =", newTerm)
	cm.setServerState(FOLLOWER)
	err := cm.RaftPersistentState.SetCurrentTerm(newTerm)
	if err != nil {
		return err
	}
	return nil
}

// -- leader code

func (cm *PassiveConsensusModule) sendAppendEntriesToAllPeers(empty bool) error {
	currentTerm := cm.RaftPersistentState.GetCurrentTerm()
	commitIndex := cm.commitIndex.UnsafeGet()
	//
	return cm.ClusterInfo.ForEachPeer(
		func(serverId ServerId) error {
			fm, err := cm.LeaderVolatileState.GetFollowerManager(serverId)
			if err != nil {
				return err
			}
			return fm.SendAppendEntriesToPeerAsync(
				empty,
				currentTerm,
				commitIndex,
			)
		},
	)
}

// #RFS-L4: If there exists an N such that N > commitIndex, a majority
// of matchIndex[i] >= N, and log[N].term == currentTerm:
// set commitIndex = N (#5.3, #5.4)
func (cm *PassiveConsensusModule) advanceCommitIndexIfPossible() error {
	commitIndex := cm.commitIndex.UnsafeGet()
	newerCommitIndex, err := cm.LeaderVolatileState.FindNewerCommitIndex(
		cm.ClusterInfo,
		cm.logRO,
		cm.RaftPersistentState.GetCurrentTerm(),
		commitIndex,
	)
	if err != nil {
		return err
	}
	if newerCommitIndex != 0 && newerCommitIndex > commitIndex {
		err = cm.setCommitIndex(newerCommitIndex)
		if err != nil {
			return err
		}
	}
	return nil
}

// Wrapper for the call to Log.SetEntriesAfterIndex()
func (cm *PassiveConsensusModule) setEntriesAfterIndex(li LogIndex, entries []LogEntry) error {
	commitIndex := cm.commitIndex.UnsafeGet()
	// Check that we're not trying to rewind past commitIndex
	if li < commitIndex {
		return fmt.Errorf(
			"FATAL: setEntriesAfterIndex(%d, ...) but commitIndex=%d", li, commitIndex,
		)
	}
	newIole := li + LogIndex(len(entries))
	if newIole < commitIndex {
		return fmt.Errorf(
			"FATAL: setEntriesAfterIndex(%d, ...) would set iole=%d < commitIndex=%d",
			li,
			newIole,
			commitIndex,
		)
	}
	return cm.logWO.SetEntriesAfterIndex(li, entries)
}
<commit_msg>NewPassiveConsensusModule checks more params<commit_after>package consensus

import (
	"errors"
	"fmt"
	"log"
	"sync"
	"time"

	. "github.com/divtxt/raft"
	"github.com/divtxt/raft/config"
	"github.com/divtxt/raft/consensus/candidate"
	"github.com/divtxt/raft/consensus/leader"
	"github.com/divtxt/raft/internal"
	"github.com/divtxt/raft/logindex"
	"github.com/divtxt/raft/util"
)

type PassiveConsensusModule struct {
	lock *sync.Mutex

	// ===== the following fields meant to be immutable

	// -- External components
	RaftPersistentState         RaftPersistentState
	logRO                       internal.LogTailOnlyRO
	logWO                       internal.LogTailOnlyWO
	sendOnlyRpcRequestVoteAsync internal.SendOnlyRpcRequestVoteAsync
	aeSender                    internal.IAppendEntriesSender
	logger                      *log.Logger

	// -- Config
	ClusterInfo *config.ClusterInfo

	// ===== the following fields are mutable

	// -- State - for all servers
	serverState ServerState

	// commitIndex is the index of highest log entry known to be committed
	// (initialized to 0, increases monotonically)
	commitIndex            *logindex.WatchedIndex
	electionTimeoutChooser *util.ElectionTimeoutChooser
	ElectionTimeoutTimer   *util.Timer

	// -- State - for candidates only
	CandidateVolatileState *candidate.CandidateVolatileState

	// -- State - for leaders only
	LeaderVolatileState *leader.LeaderVolatileState
}

func NewPassiveConsensusModule(
	raftPersistentState RaftPersistentState,
	log internal.LogTailOnly,
	sendOnlyRpcRequestVoteAsync internal.SendOnlyRpcRequestVoteAsync,
	aeSender internal.IAppendEntriesSender,
	clusterInfo *config.ClusterInfo,
	electionTimeoutLow time.Duration,
	nowFunc func() time.Time,
	logger *log.Logger,
) (*PassiveConsensusModule, error) {
	// Param checks
	if raftPersistentState == nil {
		return nil, errors.New("'raftPersistentState' cannot be nil")
	}
	if log == nil {
		return nil, errors.New("'log' cannot be nil")
	}
	if sendOnlyRpcRequestVoteAsync == nil {
		return nil, errors.New("'sendOnlyRpcRequestVoteAsync' cannot be nil")
	}
	if clusterInfo == nil {
		return nil, errors.New("clusterInfo cannot be nil")
	}
	if electionTimeoutLow.Nanoseconds() <= 0 {
		return nil, errors.New("electionTimeoutLow must be greater than zero")
	}
	if nowFunc == nil {
		return nil, errors.New("'nowFunc' cannot be nil")
	}
	if logger == nil {
		return nil, errors.New("'logger' cannot be nil")
	}

	lock := &sync.Mutex{}
	electionTimeoutTimer := util.NewTimer(electionTimeoutLow, nowFunc)

	pcm := &PassiveConsensusModule{
		lock,

		// -- External components
		raftPersistentState,
		log,
		log,
		sendOnlyRpcRequestVoteAsync,
		aeSender,
		logger,

		// -- Config
		clusterInfo,

		// -- State - for all servers
		// #5.2-p1s2: When servers start up, they begin as followers
		FOLLOWER,

		// -- State - for all servers
		// commitIndex is the index of highest log entry known to be committed
		// (initialized to 0, increases monotonically)
		logindex.NewWatchedIndex(lock),
		util.NewElectionTimeoutChooser(electionTimeoutLow),
		electionTimeoutTimer,

		// -- State - for candidates only
		nil,

		// -- State - for leaders only
		nil,
	}

	return pcm, nil
}

// Get the current server state.
// Validates the server state before returning.
func (cm *PassiveConsensusModule) GetServerState() ServerState {
	cm.lock.Lock()
	defer cm.lock.Unlock()

	return cm.serverState
}

// Set the current server state.
// Validates the server state before setting.
func (cm *PassiveConsensusModule) setServerState(serverState ServerState) {
	if serverState != FOLLOWER && serverState != CANDIDATE && serverState != LEADER {
		panic(fmt.Sprintf("FATAL: unknown ServerState: %v", serverState))
	}
	if cm.serverState != serverState {
		cm.logger.Println(
			"[raft] setServerState:",
			ServerStateToString(cm.serverState),
			"->",
			ServerStateToString(serverState),
		)
		cm.serverState = serverState
	}
}

// Get the current commitIndex value.
func (cm *PassiveConsensusModule) GetCommitIndex() LogIndex {
	cm.lock.Lock()
	defer cm.lock.Unlock()

	return cm.commitIndex.UnsafeGet()
}

// Get the commitIndex WatchableIndex.
func (cm *PassiveConsensusModule) GetCommitIndexWatchable() WatchableIndex {
	// No need to lock since this is an immutable field.
	return cm.commitIndex
}

// Set the current commitIndex value.
// Checks that it is does not reduce.
func (cm *PassiveConsensusModule) setCommitIndex(commitIndex LogIndex) error {
	if commitIndex < cm.commitIndex.UnsafeGet() {
		return fmt.Errorf(
			"setCommitIndex to %v < current commitIndex %v",
			commitIndex,
			cm.commitIndex.UnsafeGet(),
		)
	}
	// FIXME: check against lastCompacted as well!
	iole := cm.logRO.GetIndexOfLastEntry()
	if commitIndex > iole {
		return fmt.Errorf(
			"setCommitIndex to %v > current indexOfLastEntry %v",
			commitIndex,
			iole,
		)
	}
	err := cm.commitIndex.UnsafeSet(commitIndex)
	return err
}

// AppendCommand appends the given serialized command to the Raft log and returns
// the index of the appended entry.
func (cm *PassiveConsensusModule) AppendCommand(command Command) (LogIndex, error) {
	cm.lock.Lock()
	defer cm.lock.Unlock()

	if cm.serverState != LEADER {
		return 0, ErrNotLeader
	}

	termNo := cm.RaftPersistentState.GetCurrentTerm()
	logEntry := LogEntry{termNo, command}
	return cm.logWO.AppendEntry(logEntry)
}

// Iterate
func (cm *PassiveConsensusModule) Tick() error {
	cm.lock.Lock()
	defer cm.lock.Unlock()

	switch cm.serverState {
	case FOLLOWER:
		// #RFS-F2: If election timeout elapses without receiving
		// AppendEntries RPC from current leader or granting vote
		// to candidate: convert to candidate
		// #5.2-p1s5: If a follower receives no communication over a period
		// of time called the election timeout, then it assumes there is no
		// viable leader and begins an election to choose a new leader.
		fallthrough
	case CANDIDATE:
		// #RFS-C4: If election timeout elapses: start new election
		// #5.2-p5s1: The third possible outcome is that a candidate neither
		// wins nor loses the election; ... votes could be split so that no
		// candidate obtains a majority.
		// #5.2-p5s2: When this happens, each candidate will time out and
		// start a new election by incrementing its term and initiating
		// another round of RequestVote RPCs.
		if cm.ElectionTimeoutTimer.Expired() {
			cm.logger.Println("[raft] Election timeout - starting a new election")
			err := cm.becomeCandidateAndBeginElection()
			if err != nil {
				return err
			}
			// *** SOLO ***
			// Single node cluster wins election immediately since it has all the votes
			// But don't skip the election process, mainly since it increases current term!
			if cm.ClusterInfo.GetClusterSize() == 1 {
				cm.logger.Println("[raft] Single node cluster - win election immediately!")
				err := cm.becomeLeader()
				if err != nil {
					return err
				}
			}
		}
	case LEADER:
		// #RFS-L4: If there exists an N such that N > commitIndex, a majority
		// of matchIndex[i] >= N, and log[N].term == currentTerm:
		// set commitIndex = N (#5.3, #5.4)
		err := cm.advanceCommitIndexIfPossible()
		if err != nil {
			return err
		}
		// #RFS-L3.0: If last log index >= nextIndex for a follower: send
		// AppendEntries RPC with log entries starting at nextIndex
		err = cm.sendAppendEntriesToAllPeers(false)
		if err != nil {
			return err
		}
	}
	return nil
}

func (cm *PassiveConsensusModule) becomeCandidateAndBeginElection() error {
	// #RFS-C1: On conversion to candidate, start election:
	// Increment currentTerm; Vote for self; Send RequestVote RPCs
	// to all other servers; Reset election timer
	// #5.2-p2s1: To begin an election, a follower increments its
	// current term and transitions to candidate state.
	newTerm := cm.RaftPersistentState.GetCurrentTerm() + 1
	err := cm.RaftPersistentState.SetCurrentTerm(newTerm)
	if err != nil {
		return err
	}
	cm.CandidateVolatileState, err = candidate.NewCandidateVolatileState(cm.ClusterInfo)
	if err != nil {
		return err
	}
	// FIXME: return newTerm to avoid logging here
	cm.logger.Println("[raft] becomeCandidateAndBeginElection: newTerm =", newTerm)
	cm.setServerState(CANDIDATE)
	// #5.2-p2s2: It then votes for itself and issues RequestVote RPCs
	// in parallel to each of the other servers in the cluster.
	err = cm.RaftPersistentState.SetVotedFor(cm.ClusterInfo.GetThisServerId())
	if err != nil {
		return err
	}
	lastLogIndex, lastLogTerm, err := GetIndexAndTermOfLastEntry(cm.logRO)
	if err != nil {
		return err
	}
	err = cm.ClusterInfo.ForEachPeer(
		func(serverId ServerId) error {
			rpcRequestVote := &RpcRequestVote{newTerm, lastLogIndex, lastLogTerm}
			cm.sendOnlyRpcRequestVoteAsync(serverId, rpcRequestVote)
			return nil
		},
	)
	if err != nil {
		return err
	}
	// Reset election timeout!
	cm.ElectionTimeoutTimer.RestartWithDuration(
		cm.electionTimeoutChooser.ChooseRandomElectionTimeout(),
	)
	return nil
}

func (cm *PassiveConsensusModule) becomeLeader() error {
	iole := cm.logRO.GetIndexOfLastEntry()
	var err error
	cm.LeaderVolatileState, err = leader.NewLeaderVolatileState(cm.ClusterInfo, iole, cm.aeSender)
	if err != nil {
		return err
	}
	cm.logger.Println(
		"[raft] becomeLeader: iole =", iole, ", commitIndex =", cm.commitIndex.UnsafeGet(),
	)
	cm.setServerState(LEADER)
	// #RFS-L1a: Upon election: send initial empty AppendEntries RPCs (heartbeat)
	// to each server;
	err = cm.sendAppendEntriesToAllPeers(true)
	if err != nil {
		return err
	}
	return nil
}

func (cm *PassiveConsensusModule) becomeFollowerWithTerm(newTerm TermNo) error {
	currentTerm := cm.RaftPersistentState.GetCurrentTerm()
	if cm.serverState == FOLLOWER && currentTerm == newTerm {
		// Nothing to change!
		return nil
	}
	cm.logger.Println("[raft] becomeFollowerWithTerm: newTerm =", newTerm)
	cm.setServerState(FOLLOWER)
	err := cm.RaftPersistentState.SetCurrentTerm(newTerm)
	if err != nil {
		return err
	}
	return nil
}

// -- leader code

func (cm *PassiveConsensusModule) sendAppendEntriesToAllPeers(empty bool) error {
	currentTerm := cm.RaftPersistentState.GetCurrentTerm()
	commitIndex := cm.commitIndex.UnsafeGet()
	//
	return cm.ClusterInfo.ForEachPeer(
		func(serverId ServerId) error {
			fm, err := cm.LeaderVolatileState.GetFollowerManager(serverId)
			if err != nil {
				return err
			}
			return fm.SendAppendEntriesToPeerAsync(
				empty,
				currentTerm,
				commitIndex,
			)
		},
	)
}

// #RFS-L4: If there exists an N such that N > commitIndex, a majority
// of matchIndex[i] >= N, and log[N].term == currentTerm:
// set commitIndex = N (#5.3, #5.4)
func (cm *PassiveConsensusModule) advanceCommitIndexIfPossible() error {
	commitIndex := cm.commitIndex.UnsafeGet()
	newerCommitIndex, err := cm.LeaderVolatileState.FindNewerCommitIndex(
		cm.ClusterInfo,
		cm.logRO,
		cm.RaftPersistentState.GetCurrentTerm(),
		commitIndex,
	)
	if err != nil {
		return err
	}
	if newerCommitIndex != 0 && newerCommitIndex > commitIndex {
		err = cm.setCommitIndex(newerCommitIndex)
		if err != nil {
			return err
		}
	}
	return nil
}

// Wrapper for the call to Log.SetEntriesAfterIndex()
func (cm *PassiveConsensusModule) setEntriesAfterIndex(li LogIndex, entries []LogEntry) error {
	commitIndex := cm.commitIndex.UnsafeGet()
	// Check that we're not trying to rewind past commitIndex
	if li < commitIndex {
		return fmt.Errorf(
			"FATAL: setEntriesAfterIndex(%d, ...) but commitIndex=%d", li, commitIndex,
		)
	}
	newIole := li + LogIndex(len(entries))
	if newIole < commitIndex {
		return fmt.Errorf(
			"FATAL: setEntriesAfterIndex(%d, ...) would set iole=%d < commitIndex=%d",
			li,
			newIole,
			commitIndex,
		)
	}
	return cm.logWO.SetEntriesAfterIndex(li, entries)
}
<|endoftext|>


<commit_before>// Copyright 2018 Google Inc.
//
// 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 container defines a type that wraps other containers or widgets.

The container supports splitting container into sub containers, defining
container styles and placing widgets. The container also creates and manages
canvases assigned to the placed widgets.
*/
package container

import (
	"fmt"
	"image"
	"sync"

	"github.com/mum4k/termdash/internal/align"
	"github.com/mum4k/termdash/internal/area"
	"github.com/mum4k/termdash/internal/draw"
	"github.com/mum4k/termdash/internal/event"
	"github.com/mum4k/termdash/internal/terminalapi"
	"github.com/mum4k/termdash/internal/widgetapi"
)

// Container wraps either sub containers or widgets and positions them on the
// terminal.
// This is thread-safe.
type Container struct {
	// parent is the parent container, nil if this is the root container.
	parent *Container
	// The sub containers, if these aren't nil, the widget must be.
	first  *Container
	second *Container

	// term is the terminal this container is placed on.
	// All containers in the tree share the same terminal.
	term terminalapi.Terminal

	// focusTracker tracks the active (focused) container.
	// All containers in the tree share the same tracker.
	focusTracker *focusTracker

	// area is the area of the terminal this container has access to.
	area image.Rectangle

	// opts are the options provided to the container.
	opts *options

	// mu protects the container tree.
	// All containers in the tree share the same lock.
	mu *sync.Mutex
}

// String represents the container metadata in a human readable format.
// Implements fmt.Stringer.
func (c *Container) String() string {
	return fmt.Sprintf("Container@%p{parent:%p, first:%p, second:%p, area:%+v}", c, c.parent, c.first, c.second, c.area)
}

// New returns a new root container that will use the provided terminal and
// applies the provided options.
func New(t terminalapi.Terminal, opts ...Option) (*Container, error) {
	size := t.Size()
	root := &Container{
		term: t,
		// The root container has access to the entire terminal.
		area: image.Rect(0, 0, size.X, size.Y),
		opts: newOptions( /* parent = */ nil),
		mu:   &sync.Mutex{},
	}

	// Initially the root is focused.
	root.focusTracker = newFocusTracker(root)
	if err := applyOptions(root, opts...); err != nil {
		return nil, err
	}
	return root, nil
}

// newChild creates a new child container of the given parent.
func newChild(parent *Container, area image.Rectangle) *Container {
	return &Container{
		parent:       parent,
		term:         parent.term,
		focusTracker: parent.focusTracker,
		area:         area,
		opts:         newOptions(parent.opts),
		mu:           parent.mu,
	}
}

// hasBorder determines if this container has a border.
func (c *Container) hasBorder() bool {
	return c.opts.border != draw.LineStyleNone
}

// hasWidget determines if this container has a widget.
func (c *Container) hasWidget() bool {
	return c.opts.widget != nil
}

// usable returns the usable area in this container.
// This depends on whether the container has a border, etc.
func (c *Container) usable() image.Rectangle {
	if c.hasBorder() {
		return area.ExcludeBorder(c.area)
	}
	return c.area
}

// widgetArea returns the area in the container that is available for the
// widget's canvas. Takes the container border, widget's requested maximum size
// and ratio and container's alignment into account.
// Returns a zero area if the container has no widget.
func (c *Container) widgetArea() (image.Rectangle, error) {
	if !c.hasWidget() {
		return image.ZR, nil
	}

	adjusted := c.usable()
	wOpts := c.opts.widget.Options()

	if maxX := wOpts.MaximumSize.X; maxX > 0 && adjusted.Dx() > maxX {
		adjusted.Max.X -= adjusted.Dx() - maxX
	}
	if maxY := wOpts.MaximumSize.Y; maxY > 0 && adjusted.Dy() > maxY {
		adjusted.Max.Y -= adjusted.Dy() - maxY
	}

	if wOpts.Ratio.X > 0 && wOpts.Ratio.Y > 0 {
		adjusted = area.WithRatio(adjusted, wOpts.Ratio)
	}
	adjusted, err := align.Rectangle(c.usable(), adjusted, c.opts.hAlign, c.opts.vAlign)
	if err != nil {
		return image.ZR, err
	}
	return adjusted, nil
}

// split splits the container's usable area into child areas.
// Panics if the container isn't configured for a split.
func (c *Container) split() (image.Rectangle, image.Rectangle, error) {
	ar := c.usable()
	if c.opts.split == splitTypeVertical {
		return area.VSplit(ar, c.opts.splitPercent)
	}
	return area.HSplit(ar, c.opts.splitPercent)
}

// createFirst creates and returns the first sub container of this container.
func (c *Container) createFirst() (*Container, error) {
	ar, _, err := c.split()
	if err != nil {
		return nil, err
	}
	c.first = newChild(c, ar)
	return c.first, nil
}

// createSecond creates and returns the second sub container of this container.
func (c *Container) createSecond() (*Container, error) {
	_, ar, err := c.split()
	if err != nil {
		return nil, err
	}
	c.second = newChild(c, ar)
	return c.second, nil
}

// Draw draws this container and all of its sub containers.
func (c *Container) Draw() error {
	c.mu.Lock()
	defer c.mu.Unlock()
	return drawTree(c)
}

// updateFocus processes the mouse event and determines if it changes the
// focused container.
func (c *Container) updateFocus(m *terminalapi.Mouse) {
	c.mu.Lock()
	defer c.mu.Unlock()

	target := pointCont(c, m.Position)
	if target == nil { // Ignore mouse clicks where no containers are.
		return
	}
	c.focusTracker.mouse(target, m)
}

// keyboardToWidget forwards the keyboard event to the widget unconditionally.
func (c *Container) keyboardToWidget(k *terminalapi.Keyboard, scope widgetapi.KeyScope) error {
	c.mu.Lock()
	defer c.mu.Unlock()

	if scope == widgetapi.KeyScopeFocused && !c.focusTracker.isActive(c) {
		return nil
	}
	return c.opts.widget.Keyboard(k)
}

// mouseToWidget forwards the mouse event to the widget.
func (c *Container) mouseToWidget(m *terminalapi.Mouse, scope widgetapi.MouseScope) error {
	c.mu.Lock()
	defer c.mu.Unlock()

	target := pointCont(c, m.Position)
	if target == nil { // Ignore mouse clicks where no containers are.
		return nil
	}

	// Ignore clicks falling outside of the container.
	if scope != widgetapi.MouseScopeGlobal && !m.Position.In(c.area) {
		return nil
	}

	// Ignore clicks falling outside of the widget's canvas.
	wa, err := c.widgetArea()
	if err != nil {
		return err
	}
	if scope == widgetapi.MouseScopeWidget && !m.Position.In(wa) {
		return nil
	}

	// The sent mouse coordinate is relative to the widget canvas, i.e. zero
	// based, even though the widget might not be in the top left corner on the
	// terminal.
	offset := wa.Min
	var wm *terminalapi.Mouse
	if m.Position.In(wa) {
		wm = &terminalapi.Mouse{
			Position: m.Position.Sub(offset),
			Button:   m.Button,
		}
	} else {
		wm = &terminalapi.Mouse{
			Position: image.Point{-1, -1},
			Button:   m.Button,
		}
	}
	return c.opts.widget.Mouse(wm)
}

// Subscribe tells the container to subscribe itself and widgets to the
// provided event distribution system.
func (c *Container) Subscribe(eds *event.DistributionSystem) {
	c.mu.Lock()
	defer c.mu.Unlock()

	// maxReps is the maximum number of repetitive events towards widgets
	// before we throttle them.
	const maxReps = 10

	root := rootCont(c)
	// Subscriber the container itself in order to track keyboard focus.
	eds.Subscribe([]terminalapi.Event{&terminalapi.Mouse{}}, func(ev terminalapi.Event) {
		root.updateFocus(ev.(*terminalapi.Mouse))
	}, event.MaxRepetitive(0)) // One event is enough to change the focus.

	// Subscribe any widgets that specify Keyboard or Mouse in their options.
	var errStr string
	preOrder(root, &errStr, visitFunc(func(c *Container) error {
		if c.hasWidget() {
			wOpt := c.opts.widget.Options()
			switch scope := wOpt.WantKeyboard; scope {
			case widgetapi.KeyScopeNone:
				// Widget doesn't want any keyboard events.

			default:
				eds.Subscribe([]terminalapi.Event{&terminalapi.Keyboard{}}, func(ev terminalapi.Event) {
					if err := c.keyboardToWidget(ev.(*terminalapi.Keyboard), scope); err != nil {
						eds.Event(terminalapi.NewErrorf("failed to send global keyboard event %v to widget %T: %v", ev, c.opts.widget, err))
					}
				}, event.MaxRepetitive(maxReps))
			}

			switch scope := wOpt.WantMouse; scope {
			case widgetapi.MouseScopeNone:
				// Widget doesn't want any mouse events.

			default:
				eds.Subscribe([]terminalapi.Event{&terminalapi.Mouse{}}, func(ev terminalapi.Event) {
					if err := c.mouseToWidget(ev.(*terminalapi.Mouse), scope); err != nil {
						eds.Event(terminalapi.NewErrorf("failed to send mouse event %v to widget %T: %v", ev, c.opts.widget, err))
					}
				}, event.MaxRepetitive(maxReps))
			}
		}
		return nil
	}))
}
<commit_msg>Documenting non-public method of container.<commit_after>// Copyright 2018 Google Inc.
//
// 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 container defines a type that wraps other containers or widgets.

The container supports splitting container into sub containers, defining
container styles and placing widgets. The container also creates and manages
canvases assigned to the placed widgets.
*/
package container

import (
	"fmt"
	"image"
	"sync"

	"github.com/mum4k/termdash/internal/align"
	"github.com/mum4k/termdash/internal/area"
	"github.com/mum4k/termdash/internal/draw"
	"github.com/mum4k/termdash/internal/event"
	"github.com/mum4k/termdash/internal/terminalapi"
	"github.com/mum4k/termdash/internal/widgetapi"
)

// Container wraps either sub containers or widgets and positions them on the
// terminal.
// This is thread-safe.
type Container struct {
	// parent is the parent container, nil if this is the root container.
	parent *Container
	// The sub containers, if these aren't nil, the widget must be.
	first  *Container
	second *Container

	// term is the terminal this container is placed on.
	// All containers in the tree share the same terminal.
	term terminalapi.Terminal

	// focusTracker tracks the active (focused) container.
	// All containers in the tree share the same tracker.
	focusTracker *focusTracker

	// area is the area of the terminal this container has access to.
	area image.Rectangle

	// opts are the options provided to the container.
	opts *options

	// mu protects the container tree.
	// All containers in the tree share the same lock.
	mu *sync.Mutex
}

// String represents the container metadata in a human readable format.
// Implements fmt.Stringer.
func (c *Container) String() string {
	return fmt.Sprintf("Container@%p{parent:%p, first:%p, second:%p, area:%+v}", c, c.parent, c.first, c.second, c.area)
}

// New returns a new root container that will use the provided terminal and
// applies the provided options.
func New(t terminalapi.Terminal, opts ...Option) (*Container, error) {
	size := t.Size()
	root := &Container{
		term: t,
		// The root container has access to the entire terminal.
		area: image.Rect(0, 0, size.X, size.Y),
		opts: newOptions( /* parent = */ nil),
		mu:   &sync.Mutex{},
	}

	// Initially the root is focused.
	root.focusTracker = newFocusTracker(root)
	if err := applyOptions(root, opts...); err != nil {
		return nil, err
	}
	return root, nil
}

// newChild creates a new child container of the given parent.
func newChild(parent *Container, area image.Rectangle) *Container {
	return &Container{
		parent:       parent,
		term:         parent.term,
		focusTracker: parent.focusTracker,
		area:         area,
		opts:         newOptions(parent.opts),
		mu:           parent.mu,
	}
}

// hasBorder determines if this container has a border.
func (c *Container) hasBorder() bool {
	return c.opts.border != draw.LineStyleNone
}

// hasWidget determines if this container has a widget.
func (c *Container) hasWidget() bool {
	return c.opts.widget != nil
}

// usable returns the usable area in this container.
// This depends on whether the container has a border, etc.
func (c *Container) usable() image.Rectangle {
	if c.hasBorder() {
		return area.ExcludeBorder(c.area)
	}
	return c.area
}

// widgetArea returns the area in the container that is available for the
// widget's canvas. Takes the container border, widget's requested maximum size
// and ratio and container's alignment into account.
// Returns a zero area if the container has no widget.
func (c *Container) widgetArea() (image.Rectangle, error) {
	if !c.hasWidget() {
		return image.ZR, nil
	}

	adjusted := c.usable()
	wOpts := c.opts.widget.Options()

	if maxX := wOpts.MaximumSize.X; maxX > 0 && adjusted.Dx() > maxX {
		adjusted.Max.X -= adjusted.Dx() - maxX
	}
	if maxY := wOpts.MaximumSize.Y; maxY > 0 && adjusted.Dy() > maxY {
		adjusted.Max.Y -= adjusted.Dy() - maxY
	}

	if wOpts.Ratio.X > 0 && wOpts.Ratio.Y > 0 {
		adjusted = area.WithRatio(adjusted, wOpts.Ratio)
	}
	adjusted, err := align.Rectangle(c.usable(), adjusted, c.opts.hAlign, c.opts.vAlign)
	if err != nil {
		return image.ZR, err
	}
	return adjusted, nil
}

// split splits the container's usable area into child areas.
// Panics if the container isn't configured for a split.
func (c *Container) split() (image.Rectangle, image.Rectangle, error) {
	ar := c.usable()
	if c.opts.split == splitTypeVertical {
		return area.VSplit(ar, c.opts.splitPercent)
	}
	return area.HSplit(ar, c.opts.splitPercent)
}

// createFirst creates and returns the first sub container of this container.
func (c *Container) createFirst() (*Container, error) {
	ar, _, err := c.split()
	if err != nil {
		return nil, err
	}
	c.first = newChild(c, ar)
	return c.first, nil
}

// createSecond creates and returns the second sub container of this container.
func (c *Container) createSecond() (*Container, error) {
	_, ar, err := c.split()
	if err != nil {
		return nil, err
	}
	c.second = newChild(c, ar)
	return c.second, nil
}

// Draw draws this container and all of its sub containers.
func (c *Container) Draw() error {
	c.mu.Lock()
	defer c.mu.Unlock()
	return drawTree(c)
}

// updateFocus processes the mouse event and determines if it changes the
// focused container.
func (c *Container) updateFocus(m *terminalapi.Mouse) {
	c.mu.Lock()
	defer c.mu.Unlock()

	target := pointCont(c, m.Position)
	if target == nil { // Ignore mouse clicks where no containers are.
		return
	}
	c.focusTracker.mouse(target, m)
}

// keyboardToWidget forwards the keyboard event to the widget unconditionally.
func (c *Container) keyboardToWidget(k *terminalapi.Keyboard, scope widgetapi.KeyScope) error {
	c.mu.Lock()
	defer c.mu.Unlock()

	if scope == widgetapi.KeyScopeFocused && !c.focusTracker.isActive(c) {
		return nil
	}
	return c.opts.widget.Keyboard(k)
}

// mouseToWidget forwards the mouse event to the widget.
func (c *Container) mouseToWidget(m *terminalapi.Mouse, scope widgetapi.MouseScope) error {
	c.mu.Lock()
	defer c.mu.Unlock()

	target := pointCont(c, m.Position)
	if target == nil { // Ignore mouse clicks where no containers are.
		return nil
	}

	// Ignore clicks falling outside of the container.
	if scope != widgetapi.MouseScopeGlobal && !m.Position.In(c.area) {
		return nil
	}

	// Ignore clicks falling outside of the widget's canvas.
	wa, err := c.widgetArea()
	if err != nil {
		return err
	}
	if scope == widgetapi.MouseScopeWidget && !m.Position.In(wa) {
		return nil
	}

	// The sent mouse coordinate is relative to the widget canvas, i.e. zero
	// based, even though the widget might not be in the top left corner on the
	// terminal.
	offset := wa.Min
	var wm *terminalapi.Mouse
	if m.Position.In(wa) {
		wm = &terminalapi.Mouse{
			Position: m.Position.Sub(offset),
			Button:   m.Button,
		}
	} else {
		wm = &terminalapi.Mouse{
			Position: image.Point{-1, -1},
			Button:   m.Button,
		}
	}
	return c.opts.widget.Mouse(wm)
}

// Subscribe tells the container to subscribe itself and widgets to the
// provided event distribution system.
// This method is private to termdash, stability isn't guaranteed and changes
// won't be backward compatible.
func (c *Container) Subscribe(eds *event.DistributionSystem) {
	c.mu.Lock()
	defer c.mu.Unlock()

	// maxReps is the maximum number of repetitive events towards widgets
	// before we throttle them.
	const maxReps = 10

	root := rootCont(c)
	// Subscriber the container itself in order to track keyboard focus.
	eds.Subscribe([]terminalapi.Event{&terminalapi.Mouse{}}, func(ev terminalapi.Event) {
		root.updateFocus(ev.(*terminalapi.Mouse))
	}, event.MaxRepetitive(0)) // One event is enough to change the focus.

	// Subscribe any widgets that specify Keyboard or Mouse in their options.
	var errStr string
	preOrder(root, &errStr, visitFunc(func(c *Container) error {
		if c.hasWidget() {
			wOpt := c.opts.widget.Options()
			switch scope := wOpt.WantKeyboard; scope {
			case widgetapi.KeyScopeNone:
				// Widget doesn't want any keyboard events.

			default:
				eds.Subscribe([]terminalapi.Event{&terminalapi.Keyboard{}}, func(ev terminalapi.Event) {
					if err := c.keyboardToWidget(ev.(*terminalapi.Keyboard), scope); err != nil {
						eds.Event(terminalapi.NewErrorf("failed to send global keyboard event %v to widget %T: %v", ev, c.opts.widget, err))
					}
				}, event.MaxRepetitive(maxReps))
			}

			switch scope := wOpt.WantMouse; scope {
			case widgetapi.MouseScopeNone:
				// Widget doesn't want any mouse events.

			default:
				eds.Subscribe([]terminalapi.Event{&terminalapi.Mouse{}}, func(ev terminalapi.Event) {
					if err := c.mouseToWidget(ev.(*terminalapi.Mouse), scope); err != nil {
						eds.Event(terminalapi.NewErrorf("failed to send mouse event %v to widget %T: %v", ev, c.opts.widget, err))
					}
				}, event.MaxRepetitive(maxReps))
			}
		}
		return nil
	}))
}
<|endoftext|>


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

package vlog

import (
	"github.com/cosnicolaou/llog"
)

// Info logs to the INFO log.
// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
func Info(args ...interface{}) {
	Log.log.Print(llog.InfoLog, args...)
	Log.maybeFlush()
}

// Infof logs to the INFO log.
// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
func Infof(format string, args ...interface{}) {
	fn := Log.log.Printf // needed to avoid go vet warning
	fn(llog.InfoLog, format, args...)
	Log.maybeFlush()
}

// InfoDepth acts as Info but uses depth to determine which call frame to log.
// A depth of 0 is equivalent to calling Info.
func InfoDepth(depth int, args ...interface{}) {
	Log.log.PrintDepth(llog.InfoLog, depth, args...)
	Log.maybeFlush()
}

// InfoStack logs the current goroutine's stack if the all parameter
// is false, or the stacks of all goroutines if it's true.
func InfoStack(all bool) {
	infoStack(Log, all)
}

// V returns true if the configured logging level is greater than or equal to its parameter
func V(level Level) bool {
	return Log.log.VDepth(0, llog.Level(level))
}

// VI is like V, except that it returns an instance of the Info
// interface that will either log (if level >= the configured level)
// or discard its parameters. This allows for logger.VI(2).Info
// style usage.
func VI(level Level) interface {
	// Info logs to the INFO log.
	// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
	Info(args ...interface{})

	// Infoln logs to the INFO log.
	// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
	Infof(format string, args ...interface{})

	// InfoDepth acts as Info but uses depth to determine which call frame to log.
	// A depth of 0 is equivalent to calling Info.
	InfoDepth(depth int, args ...interface{})

	// InfoStack logs the current goroutine's stack if the all parameter
	// is false, or the stacks of all goroutines if it's true.
	InfoStack(all bool)
} {
	if Log.log.VDepth(0, llog.Level(level)) {
		return Log
	}
	return &discardInfo{}
}

// Flush flushes all pending log I/O.
func FlushLog() {
	Log.FlushLog()
}

// Error logs to the ERROR and INFO logs.
// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
func Error(args ...interface{}) {
	Log.log.Print(llog.ErrorLog, args...)
	Log.maybeFlush()
}

// ErrorDepth acts as Error but uses depth to determine which call frame to log.
// A depth of 0 is equivalent to calling Error.
func ErrorDepth(depth int, args ...interface{}) {
	Log.log.PrintDepth(llog.ErrorLog, depth, args...)
	Log.maybeFlush()
}

// Errorf logs to the ERROR and INFO logs.
// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
func Errorf(format string, args ...interface{}) {
	fn := Log.log.Printf // needed to avoid go vet warning
	fn(llog.ErrorLog, format, args...)
	Log.maybeFlush()
}

// Fatal logs to the FATAL, ERROR and INFO logs,
// including a stack trace of all running goroutines, then calls os.Exit(255).
// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
func Fatal(args ...interface{}) {
	Log.log.Print(llog.FatalLog, args...)
}

// FatalDepth acts as Fatal but uses depth to determine which call frame to log.
// A depth of 0 is equivalent to calling Fatal.
func FatalDepth(depth int, args ...interface{}) {
	Log.log.PrintDepth(llog.FatalLog, depth, args...)
}

// Fatalf logs to the FATAL, ERROR and INFO logs,
// including a stack trace of all running goroutines, then calls os.Exit(255).
// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
func Fatalf(format string, args ...interface{}) {
	fn := Log.log.Printf // needed to avoid go vet warning
	fn(llog.FatalLog, format, args...)
}

// ConfigureLogging configures all future logging. Some options
// may not be usable if ConfigureLogging is called from an init function,
// in which case an error will be returned. The Configured error is
// returned if ConfigureLogger has already been called unless the
// OverridePriorConfiguration options is included.
func Configure(opts ...LoggingOpts) error {
	return Log.Configure(opts...)
}

// Stats returns stats on how many lines/bytes haven been written to
// this set of logs.
func Stats() (Info, Error struct{ Lines, Bytes int64 }) {
	return Log.Stats()
}

// Panic is equivalent to Error() followed by a call to panic().
func Panic(args ...interface{}) {
	Log.Panic(args...)
}

// PanicDepth acts as Panic but uses depth to determine which call frame to log.
// A depth of 0 is equivalent to calling Panic.
func PanicDepth(depth int, args ...interface{}) {
	Log.PanicDepth(depth, args...)
}

// Panicf is equivalent to Errorf() followed by a call to panic().
func Panicf(format string, args ...interface{}) {
	Log.Panicf(format, args...)
}
<commit_msg>v.io/x/lib/vlog: fix file/line # logging<commit_after>// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package vlog

import (
	"github.com/cosnicolaou/llog"
)

// Info logs to the INFO log.
// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
func Info(args ...interface{}) {
	Log.log.Print(llog.InfoLog, args...)
	Log.maybeFlush()
}

// Infof logs to the INFO log.
// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
func Infof(format string, args ...interface{}) {
	Log.log.Printf(llog.InfoLog, format, args...)
	Log.maybeFlush()
}

// InfoDepth acts as Info but uses depth to determine which call frame to log.
// A depth of 0 is equivalent to calling Info.
func InfoDepth(depth int, args ...interface{}) {
	Log.log.PrintDepth(llog.InfoLog, depth, args...)
	Log.maybeFlush()
}

// InfoStack logs the current goroutine's stack if the all parameter
// is false, or the stacks of all goroutines if it's true.
func InfoStack(all bool) {
	infoStack(Log, all)
}

// V returns true if the configured logging level is greater than or equal to its parameter
func V(level Level) bool {
	return Log.log.VDepth(0, llog.Level(level))
}

// VI is like V, except that it returns an instance of the Info
// interface that will either log (if level >= the configured level)
// or discard its parameters. This allows for logger.VI(2).Info
// style usage.
func VI(level Level) interface {
	// Info logs to the INFO log.
	// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
	Info(args ...interface{})

	// Infoln logs to the INFO log.
	// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
	Infof(format string, args ...interface{})

	// InfoDepth acts as Info but uses depth to determine which call frame to log.
	// A depth of 0 is equivalent to calling Info.
	InfoDepth(depth int, args ...interface{})

	// InfoStack logs the current goroutine's stack if the all parameter
	// is false, or the stacks of all goroutines if it's true.
	InfoStack(all bool)
} {
	if Log.log.VDepth(0, llog.Level(level)) {
		return Log
	}
	return &discardInfo{}
}

// Flush flushes all pending log I/O.
func FlushLog() {
	Log.FlushLog()
}

// Error logs to the ERROR and INFO logs.
// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
func Error(args ...interface{}) {
	Log.log.Print(llog.ErrorLog, args...)
	Log.maybeFlush()
}

// ErrorDepth acts as Error but uses depth to determine which call frame to log.
// A depth of 0 is equivalent to calling Error.
func ErrorDepth(depth int, args ...interface{}) {
	Log.log.PrintDepth(llog.ErrorLog, depth, args...)
	Log.maybeFlush()
}

// Errorf logs to the ERROR and INFO logs.
// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
func Errorf(format string, args ...interface{}) {
	Log.log.Printf(llog.ErrorLog, format, args...)
	Log.maybeFlush()
}

// Fatal logs to the FATAL, ERROR and INFO logs,
// including a stack trace of all running goroutines, then calls os.Exit(255).
// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
func Fatal(args ...interface{}) {
	Log.log.Print(llog.FatalLog, args...)
}

// FatalDepth acts as Fatal but uses depth to determine which call frame to log.
// A depth of 0 is equivalent to calling Fatal.
func FatalDepth(depth int, args ...interface{}) {
	Log.log.PrintDepth(llog.FatalLog, depth, args...)
}

// Fatalf logs to the FATAL, ERROR and INFO logs,
// including a stack trace of all running goroutines, then calls os.Exit(255).
// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
func Fatalf(format string, args ...interface{}) {
	Log.log.Printf(llog.FatalLog, format, args...)
}

// ConfigureLogging configures all future logging. Some options
// may not be usable if ConfigureLogging is called from an init function,
// in which case an error will be returned. The Configured error is
// returned if ConfigureLogger has already been called unless the
// OverridePriorConfiguration options is included.
func Configure(opts ...LoggingOpts) error {
	return Log.Configure(opts...)
}

// Stats returns stats on how many lines/bytes haven been written to
// this set of logs.
func Stats() (Info, Error struct{ Lines, Bytes int64 }) {
	return Log.Stats()
}

// Panic is equivalent to Error() followed by a call to panic().
func Panic(args ...interface{}) {
	Log.Panic(args...)
}

// PanicDepth acts as Panic but uses depth to determine which call frame to log.
// A depth of 0 is equivalent to calling Panic.
func PanicDepth(depth int, args ...interface{}) {
	Log.PanicDepth(depth, args...)
}

// Panicf is equivalent to Errorf() followed by a call to panic().
func Panicf(format string, args ...interface{}) {
	Log.Panicf(format, args...)
}
<|endoftext|>


<commit_before>// Copyright (c) 2017, The dcrdata developers
// See LICENSE for details.

package explorer

import (
	"sync"
	"time"
)

const (
	wsWriteTimeout           = 10 * time.Second
	wsReadTimeout            = 12 * time.Second
	pingInterval             = 12 * time.Second
	sigNewBlock    hubSignal = iota
	sigMempoolUpdate
	sigPingAndUserCount
)

// WebSocketMessage represents the JSON object used to send and received typed
// messages to the web client.
type WebSocketMessage struct {
	EventId string `json:"event"`
	Message string `json:"message"`
}

// Event type field for an SSE event
var eventIDs = map[hubSignal]string{
	sigNewBlock:         "newblock",
	sigMempoolUpdate:    "mempool",
	sigPingAndUserCount: "ping",
}

// WebsocketHub and its event loop manage all websocket client connections.
// WebsocketHub is responsible for closing all connections registered with it.
// If the event loop is running, calling (*WebsocketHub).Stop() will handle it.
type WebsocketHub struct {
	sync.RWMutex
	clients         map[*hubSpoke]struct{}
	Register        chan *hubSpoke
	Unregister      chan *hubSpoke
	HubRelay        chan hubSignal
	NewBlockSummary chan BlockBasic
	quitWSHandler   chan struct{}
}

type hubSignal int
type hubSpoke chan hubSignal

// NewWebsocketHub creates a new WebsocketHub
func NewWebsocketHub() *WebsocketHub {
	return &WebsocketHub{
		clients:       make(map[*hubSpoke]struct{}),
		Register:      make(chan *hubSpoke),
		Unregister:    make(chan *hubSpoke),
		HubRelay:      make(chan hubSignal),
		quitWSHandler: make(chan struct{}),
	}
}

// NumClients returns the number of clients connected to the websocket hub
func (wsh *WebsocketHub) NumClients() int {
	return len(wsh.clients)
}

// RegisterClient registers a websocket connection with the hub.
func (wsh *WebsocketHub) RegisterClient(c *hubSpoke) {
	log.Debug("Registering new websocket client")
	wsh.Register <- c
}

// registerClient should only be called from the run loop
func (wsh *WebsocketHub) registerClient(c *hubSpoke) {
	wsh.clients[c] = struct{}{}
}

// UnregisterClient unregisters the input websocket connection via the main
// run() loop.  This call will block if the run() loop is not running.
func (wsh *WebsocketHub) UnregisterClient(c *hubSpoke) {
	wsh.Unregister <- c
}

// unregisterClient should only be called from the loop in run().
func (wsh *WebsocketHub) unregisterClient(c *hubSpoke) {
	if _, ok := wsh.clients[c]; !ok {
		// unknown client, do not close channel
		log.Warnf("unknown client")
		return
	}
	delete(wsh.clients, c)

	// Close the channel, but make sure the client didn't do it
	safeClose(*c)
}

func safeClose(cc hubSpoke) {
	select {
	case _, ok := <-cc:
		if !ok {
			log.Debug("Channel already closed!")
			return
		}
	default:
	}
	close(cc)
}

// Stop kills the run() loop and unregisteres all clients (connections).
func (wsh *WebsocketHub) Stop() {
	// end the run() loop, allowing in progress operations to complete
	wsh.quitWSHandler <- struct{}{}
	// unregister all clients
	for client := range wsh.clients {
		wsh.unregisterClient(client)
	}
}

func (wsh *WebsocketHub) run() {
	log.Info("Starting WebsocketHub run loop.")
	for {
	events:
		select {
		case hubSignal := <-wsh.HubRelay:
			switch hubSignal {
			case sigNewBlock:
				log.Infof("Signaling new block to %d clients.", len(wsh.clients))
			case sigPingAndUserCount:
				log.Tracef("Signaling ping/user count to %d clients.", len(wsh.clients))
			case sigMempoolUpdate:
				log.Infof("Signaling mempool update to %d clients.", len(wsh.clients))
			default:
				log.Errorf("Unknown hub signal: %v", hubSignal)
				break events
			}
			for client := range wsh.clients {
				// signal or unregister the client
				select {
				case *client <- hubSignal:
				default:
					go wsh.unregisterClient(client)
				}
			}
		case c := <-wsh.Register:
			wsh.registerClient(c)
		case c := <-wsh.Unregister:
			wsh.unregisterClient(c)
		case _, ok := <-wsh.quitWSHandler:
			if !ok {
				log.Error("close channel already closed. This should not happen.")
				return
			}
			close(wsh.quitWSHandler)
			return
		}
	}
}
<commit_msg>websocket: do not run unregister client asynchronously since it's not thread safe<commit_after>// Copyright (c) 2017, The dcrdata developers
// See LICENSE for details.

package explorer

import (
	"sync"
	"time"
)

const (
	wsWriteTimeout           = 10 * time.Second
	wsReadTimeout            = 12 * time.Second
	pingInterval             = 12 * time.Second
	sigNewBlock    hubSignal = iota
	sigMempoolUpdate
	sigPingAndUserCount
)

// WebSocketMessage represents the JSON object used to send and received typed
// messages to the web client.
type WebSocketMessage struct {
	EventId string `json:"event"`
	Message string `json:"message"`
}

// Event type field for an SSE event
var eventIDs = map[hubSignal]string{
	sigNewBlock:         "newblock",
	sigMempoolUpdate:    "mempool",
	sigPingAndUserCount: "ping",
}

// WebsocketHub and its event loop manage all websocket client connections.
// WebsocketHub is responsible for closing all connections registered with it.
// If the event loop is running, calling (*WebsocketHub).Stop() will handle it.
type WebsocketHub struct {
	sync.RWMutex
	clients         map[*hubSpoke]struct{}
	Register        chan *hubSpoke
	Unregister      chan *hubSpoke
	HubRelay        chan hubSignal
	NewBlockSummary chan BlockBasic
	quitWSHandler   chan struct{}
}

type hubSignal int
type hubSpoke chan hubSignal

// NewWebsocketHub creates a new WebsocketHub
func NewWebsocketHub() *WebsocketHub {
	return &WebsocketHub{
		clients:       make(map[*hubSpoke]struct{}),
		Register:      make(chan *hubSpoke),
		Unregister:    make(chan *hubSpoke),
		HubRelay:      make(chan hubSignal),
		quitWSHandler: make(chan struct{}),
	}
}

// NumClients returns the number of clients connected to the websocket hub
func (wsh *WebsocketHub) NumClients() int {
	return len(wsh.clients)
}

// RegisterClient registers a websocket connection with the hub.
func (wsh *WebsocketHub) RegisterClient(c *hubSpoke) {
	log.Debug("Registering new websocket client")
	wsh.Register <- c
}

// registerClient should only be called from the run loop
func (wsh *WebsocketHub) registerClient(c *hubSpoke) {
	wsh.clients[c] = struct{}{}
}

// UnregisterClient unregisters the input websocket connection via the main
// run() loop.  This call will block if the run() loop is not running.
func (wsh *WebsocketHub) UnregisterClient(c *hubSpoke) {
	wsh.Unregister <- c
}

// unregisterClient should only be called from the loop in run().
func (wsh *WebsocketHub) unregisterClient(c *hubSpoke) {
	if _, ok := wsh.clients[c]; !ok {
		// unknown client, do not close channel
		log.Warnf("unknown client")
		return
	}
	delete(wsh.clients, c)

	// Close the channel, but make sure the client didn't do it
	safeClose(*c)
}

func safeClose(cc hubSpoke) {
	select {
	case _, ok := <-cc:
		if !ok {
			log.Debug("Channel already closed!")
			return
		}
	default:
	}
	close(cc)
}

// Stop kills the run() loop and unregisteres all clients (connections).
func (wsh *WebsocketHub) Stop() {
	// end the run() loop, allowing in progress operations to complete
	wsh.quitWSHandler <- struct{}{}
	// unregister all clients
	for client := range wsh.clients {
		wsh.unregisterClient(client)
	}
}

func (wsh *WebsocketHub) run() {
	log.Info("Starting WebsocketHub run loop.")
	for {
	events:
		select {
		case hubSignal := <-wsh.HubRelay:
			switch hubSignal {
			case sigNewBlock:
				log.Infof("Signaling new block to %d clients.", len(wsh.clients))
			case sigPingAndUserCount:
				log.Tracef("Signaling ping/user count to %d clients.", len(wsh.clients))
			case sigMempoolUpdate:
				log.Infof("Signaling mempool update to %d clients.", len(wsh.clients))
			default:
				log.Errorf("Unknown hub signal: %v", hubSignal)
				break events
			}
			for client := range wsh.clients {
				// signal or unregister the client
				select {
				case *client <- hubSignal:
				default:
					wsh.unregisterClient(client)
				}
			}
		case c := <-wsh.Register:
			wsh.registerClient(c)
		case c := <-wsh.Unregister:
			wsh.unregisterClient(c)
		case _, ok := <-wsh.quitWSHandler:
			if !ok {
				log.Error("close channel already closed. This should not happen.")
				return
			}
			close(wsh.quitWSHandler)
			return
		}
	}
}
<|endoftext|>


<commit_before>package conf

import (
	"bytes"
	"crypto/tls"
	"errors"
	"log"
	"net/http"
	"net/mail"
	"net/smtp"

	"github.com/StackExchange/bosun/_third_party/github.com/mjibson/email"
	"github.com/StackExchange/scollector/collect"
)

func (n *Notification) Notify(subject, body []byte, from, smtpHost string, ak string, attachments ...*Attachment) {
	if len(n.Email) > 0 {
		go n.DoEmail(subject, body, from, smtpHost, ak, attachments...)
	}
	if n.Post != nil {
		go n.DoPost(subject)
	}
	if n.Get != nil {
		go n.DoGet()
	}
	if n.Print {
		go n.DoPrint(subject)
	}
}

func (n *Notification) DoPrint(subject []byte) {
	log.Println(string(subject))
}

func (n *Notification) DoPost(subject []byte) {
	if n.Body != nil {
		buf := new(bytes.Buffer)
		if err := n.Body.Execute(buf, string(subject)); err != nil {
			log.Println(err)
			return
		}
		subject = buf.Bytes()
	}
	resp, err := http.Post(n.Post.String(), "application/x-www-form-urlencoded", bytes.NewBuffer(subject))
	if resp != nil && resp.Body != nil {
		defer resp.Body.Close()
	}
	if err != nil {
		log.Println(err)
		return
	}
	if resp.StatusCode >= 300 {
		log.Println("bad response on notification post:", resp.Status)
	}
}

func (n *Notification) DoGet() {
	resp, err := http.Get(n.Get.String())
	if err != nil {
		log.Println(err)
		return
	}
	if resp.StatusCode >= 300 {
		log.Println("bad response on notification get:", resp.Status)
	}
}

type Attachment struct {
	Data        []byte
	Filename    string
	ContentType string
}

func (n *Notification) DoEmail(subject, body []byte, from, smtpHost string, ak string, attachments ...*Attachment) {
	e := email.NewEmail()
	e.From = from
	for _, a := range n.Email {
		e.To = append(e.To, a.Address)
	}
	e.Subject = string(subject)
	e.HTML = body
	for _, a := range attachments {
		e.Attach(bytes.NewBuffer(a.Data), a.Filename, a.ContentType)
	}
	if err := Send(e, smtpHost); err != nil {
		collect.Add("email.sent_failed", nil, 1)
		log.Printf("failed to send alert %v to %v %v\n", ak, e.To, err)
		return
	}
	collect.Add("email.sent", nil, 1)
	log.Printf("relayed alert %v to %v sucessfully\n", ak, e.To)
}

// Send an email using the given host and SMTP auth (optional), returns any
// error thrown by smtp.SendMail. This function merges the To, Cc, and Bcc
// fields and calls the smtp.SendMail function using the Email.Bytes() output as
// the message.
func Send(e *email.Email, addr string) error {
	// Merge the To, Cc, and Bcc fields
	to := make([]string, 0, len(e.To)+len(e.Cc)+len(e.Bcc))
	to = append(append(append(to, e.To...), e.Cc...), e.Bcc...)
	// Check to make sure there is at least one recipient and one "From" address
	if e.From == "" || len(to) == 0 {
		return errors.New("Must specify at least one From address and one To address")
	}
	from, err := mail.ParseAddress(e.From)
	if err != nil {
		return err
	}
	raw, err := e.Bytes()
	if err != nil {
		return err
	}
	return SendMail(addr, from.Address, to, raw)
}

// SendMail connects to the server at addr, switches to TLS if
// possible, authenticates with the optional mechanism a if possible,
// and then sends an email from address from, to addresses to, with
// message msg.
func SendMail(addr string, from string, to []string, msg []byte) error {
	c, err := smtp.Dial(addr)
	if err != nil {
		return err
	}
	defer c.Close()
	if err = c.Hello("localhost"); err != nil {
		return err
	}
	if ok, _ := c.Extension("STARTTLS"); ok {
		if err = c.StartTLS(&tls.Config{InsecureSkipVerify: true}); err != nil {
			return err
		}
	}
	if err = c.Mail(from); err != nil {
		return err
	}
	for _, addr := range to {
		if err = c.Rcpt(addr); err != nil {
			return err
		}
	}
	w, err := c.Data()
	if err != nil {
		return err
	}
	_, err = w.Write(msg)
	if err != nil {
		return err
	}
	err = w.Close()
	if err != nil {
		return err
	}
	return c.Quit()
}
<commit_msg>Fix non partied import<commit_after>package conf

import (
	"bytes"
	"crypto/tls"
	"errors"
	"log"
	"net/http"
	"net/mail"
	"net/smtp"

	"github.com/StackExchange/bosun/_third_party/github.com/StackExchange/scollector/collect"
	"github.com/StackExchange/bosun/_third_party/github.com/mjibson/email"
)

func (n *Notification) Notify(subject, body []byte, from, smtpHost string, ak string, attachments ...*Attachment) {
	if len(n.Email) > 0 {
		go n.DoEmail(subject, body, from, smtpHost, ak, attachments...)
	}
	if n.Post != nil {
		go n.DoPost(subject)
	}
	if n.Get != nil {
		go n.DoGet()
	}
	if n.Print {
		go n.DoPrint(subject)
	}
}

func (n *Notification) DoPrint(subject []byte) {
	log.Println(string(subject))
}

func (n *Notification) DoPost(subject []byte) {
	if n.Body != nil {
		buf := new(bytes.Buffer)
		if err := n.Body.Execute(buf, string(subject)); err != nil {
			log.Println(err)
			return
		}
		subject = buf.Bytes()
	}
	resp, err := http.Post(n.Post.String(), "application/x-www-form-urlencoded", bytes.NewBuffer(subject))
	if resp != nil && resp.Body != nil {
		defer resp.Body.Close()
	}
	if err != nil {
		log.Println(err)
		return
	}
	if resp.StatusCode >= 300 {
		log.Println("bad response on notification post:", resp.Status)
	}
}

func (n *Notification) DoGet() {
	resp, err := http.Get(n.Get.String())
	if err != nil {
		log.Println(err)
		return
	}
	if resp.StatusCode >= 300 {
		log.Println("bad response on notification get:", resp.Status)
	}
}

type Attachment struct {
	Data        []byte
	Filename    string
	ContentType string
}

func (n *Notification) DoEmail(subject, body []byte, from, smtpHost string, ak string, attachments ...*Attachment) {
	e := email.NewEmail()
	e.From = from
	for _, a := range n.Email {
		e.To = append(e.To, a.Address)
	}
	e.Subject = string(subject)
	e.HTML = body
	for _, a := range attachments {
		e.Attach(bytes.NewBuffer(a.Data), a.Filename, a.ContentType)
	}
	if err := Send(e, smtpHost); err != nil {
		collect.Add("email.sent_failed", nil, 1)
		log.Printf("failed to send alert %v to %v %v\n", ak, e.To, err)
		return
	}
	collect.Add("email.sent", nil, 1)
	log.Printf("relayed alert %v to %v sucessfully\n", ak, e.To)
}

// Send an email using the given host and SMTP auth (optional), returns any
// error thrown by smtp.SendMail. This function merges the To, Cc, and Bcc
// fields and calls the smtp.SendMail function using the Email.Bytes() output as
// the message.
func Send(e *email.Email, addr string) error {
	// Merge the To, Cc, and Bcc fields
	to := make([]string, 0, len(e.To)+len(e.Cc)+len(e.Bcc))
	to = append(append(append(to, e.To...), e.Cc...), e.Bcc...)
	// Check to make sure there is at least one recipient and one "From" address
	if e.From == "" || len(to) == 0 {
		return errors.New("Must specify at least one From address and one To address")
	}
	from, err := mail.ParseAddress(e.From)
	if err != nil {
		return err
	}
	raw, err := e.Bytes()
	if err != nil {
		return err
	}
	return SendMail(addr, from.Address, to, raw)
}

// SendMail connects to the server at addr, switches to TLS if
// possible, authenticates with the optional mechanism a if possible,
// and then sends an email from address from, to addresses to, with
// message msg.
func SendMail(addr string, from string, to []string, msg []byte) error {
	c, err := smtp.Dial(addr)
	if err != nil {
		return err
	}
	defer c.Close()
	if err = c.Hello("localhost"); err != nil {
		return err
	}
	if ok, _ := c.Extension("STARTTLS"); ok {
		if err = c.StartTLS(&tls.Config{InsecureSkipVerify: true}); err != nil {
			return err
		}
	}
	if err = c.Mail(from); err != nil {
		return err
	}
	for _, addr := range to {
		if err = c.Rcpt(addr); err != nil {
			return err
		}
	}
	w, err := c.Data()
	if err != nil {
		return err
	}
	_, err = w.Write(msg)
	if err != nil {
		return err
	}
	err = w.Close()
	if err != nil {
		return err
	}
	return c.Quit()
}
<|endoftext|>


<commit_before>/*
 * Copyright 2015 Xuyuan Pang
 * Author: Pang Xuyuan <xuyuanp # gmail dot com>
 *
 * 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 hador

import "net/http"

// Hador struct
type Hador struct {
	Router
	*FilterChain
	Logger Logger
}

// New creates new Hador instance
func New() *Hador {
	h := &Hador{
		Logger: defaultLogger,
	}
	h.Router = NewRouter(h)
	h.FilterChain = NewFilterChain(h.Router)
	return h
}

// Default creates Hador instance with default filters(LogFilter, RecoveryFilter)
func Default() *Hador {
	h := New()
	h.Before(NewLogFilter(h.Logger))
	h.Before(NewRecoveryFilter(h.Logger))
	return h
}

// Run starts serving HTTP request
func (h *Hador) Run(addr string) error {
	h.Logger.Info("Listening on %s", addr)
	return http.ListenAndServe(addr, h)
}

func (h *Hador) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	ctx := NewContext(w, req, h.Logger)
	h.Serve(ctx)
}

// Serve implements Handler interface
func (h *Hador) Serve(ctx *Context) {
	h.FilterChain.Serve(ctx)
}
<commit_msg>Added root field<commit_after>/*
 * Copyright 2015 Xuyuan Pang
 * Author: Pang Xuyuan <xuyuanp # gmail dot com>
 *
 * 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 hador

import "net/http"

// Hador struct
type Hador struct {
	Router
	*FilterChain
	Logger Logger
	root   *Node
}

// New creates new Hador instance
func New() *Hador {
	h := &Hador{
		Logger: defaultLogger,
		root:   NewNode("", 0),
	}
	h.Router = h.root
	h.FilterChain = NewFilterChain(h.Router)
	return h
}

// Default creates Hador instance with default filters(LogFilter, RecoveryFilter)
func Default() *Hador {
	h := New()
	h.Before(NewLogFilter(h.Logger))
	h.Before(NewRecoveryFilter(h.Logger))
	return h
}

// Run starts serving HTTP request
func (h *Hador) Run(addr string) error {
	h.Logger.Info("Listening on %s", addr)
	return http.ListenAndServe(addr, h)
}

func (h *Hador) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	ctx := NewContext(w, req, h.Logger)
	h.Serve(ctx)
}

// Serve implements Handler interface
func (h *Hador) Serve(ctx *Context) {
	h.FilterChain.Serve(ctx)
}
<|endoftext|>


<commit_before>package smtp

// http://www.rfc-editor.org/rfc/rfc5321.txt

import (
	"log"
	"net"
	"strings"
	"github.com/ian-kent/MailHog/mailhog"
)

type Session struct {
	conn *net.TCPConn
	line string
	conf *mailhog.Config
	state int
}

const (
	ESTABLISH = iota
	MAIL
	RCPT
	DATA
)

type Message struct {
	From string
	To string
	Data []byte
	Helo string
}

func StartSession(conn *net.TCPConn, conf *mailhog.Config) {
	conv := &Session{conn, "", conf, ESTABLISH}
	conv.log("Starting session")
	conv.Write("220", "Go-MailHog")
	conv.Read()
}

func (c *Session) log(message string, args ...interface{}) {
	message = strings.Join([]string{"[%s, %d]", message}, " ")
	args = append([]interface{}{c.conn.RemoteAddr(), c.state}, args...)
	log.Printf(message, args...)
}

func (c *Session) Read() {
	buf := make([]byte, 1024)
	n, err := c.conn.Read(buf)

	if n == 0 {
		c.log("Connection closed by remote host\n")
		return
	}
	if err != nil {
		c.log("Error reading from socket: %s\n", err)
		return
	}

	text := string(buf[0:n])
	c.log("Received %d bytes: '%s'\n", n, text)

	c.line += text

	c.Parse()
}

func (c *Session) Parse() {
	for strings.Contains(c.line, "\n") {
		parts := strings.SplitN(c.line, "\n", 2)
		if len(parts) == 2 {
			c.line = parts[1]
		} else {
			c.line = ""
		}
		c.Process(strings.Trim(parts[0], "\r\n"))
	}

	c.Read()
}

func (c *Session) Write(code string, text ...string) {
	if len(text) == 1 {
		c.conn.Write([]byte(code + " " + text[0] + "\n"))
		return
	}
	for i := 0; i < len(text) - 2; i++ {
		c.conn.Write([]byte(code + "-" + text[i] + "\n"))
	}
	c.conn.Write([]byte(code + " " + text[len(text)] + "\n"))
}

func (c *Session) Process(line string) {
	c.log("Processing line: %s", line)

	words := strings.Split(line, " ")
	command := words[0]
	c.log("In state %d, got command '%s'", c.state, command)

	switch {
		case command == "RSET":
			c.log("Got RSET command, switching to ESTABLISH state")
			c.state = ESTABLISH
			c.Write("250", "OK")
		case command == "NOOP":
			c.log("Got NOOP command")
			c.Write("250", "OK")
		case command == "QUIT":
			c.log("Got QUIT command")
			c.Write("221", "OK")
		case c.state == ESTABLISH:
			switch command {
				case "HELO":
					c.log("Got HELO command, switching to MAIL state")
					c.state = MAIL
					c.Write("250", "HELO " + c.conf.Hostname)
				case "EHLO":
					c.log("Got EHLO command, switching to MAIL state")
					c.state = MAIL
					c.Write("250", "EHLO " + c.conf.Hostname)
				default:
					c.log("Got unknown command for ESTABLISH state: '%s'", command)
			}
		case c.state == MAIL:
			switch command {
				case "MAIL":
					c.log("Got MAIL command")
				default:
					c.log("Got unknown command for MAIL state: '%s'", command)
			}
	}
}
<commit_msg>Add DATA/RCPT commands<commit_after>package smtp

// http://www.rfc-editor.org/rfc/rfc5321.txt

import (
	"log"
	"net"
	"strings"
	"github.com/ian-kent/MailHog/mailhog"
)

type Session struct {
	conn *net.TCPConn
	line string
	conf *mailhog.Config
	state int
	message *Message
}

const (
	ESTABLISH = iota
	MAIL
	RCPT
	DATA
	DONE
)

type Message struct {
	From string
	To []string
	Data string
	Helo string
}

func StartSession(conn *net.TCPConn, conf *mailhog.Config) {
	conv := &Session{conn, "", conf, ESTABLISH, &Message{}}
	conv.log("Starting session")
	conv.Write("220", conv.conf.Hostname + " ESMTP Go-MailHog")
	conv.Read()
}

func (c *Session) log(message string, args ...interface{}) {
	message = strings.Join([]string{"[%s, %d]", message}, " ")
	args = append([]interface{}{c.conn.RemoteAddr(), c.state}, args...)
	log.Printf(message, args...)
}

func (c *Session) Read() {
	buf := make([]byte, 1024)
	n, err := c.conn.Read(buf)

	if n == 0 {
		c.log("Connection closed by remote host\n")
		return
	}
	if err != nil {
		c.log("Error reading from socket: %s\n", err)
		return
	}

	text := string(buf[0:n])
	c.log("Received %d bytes: '%s'\n", n, text)

	c.line += text

	c.Parse()
}

func (c *Session) Parse() {
	for strings.Contains(c.line, "\n") {
		parts := strings.SplitN(c.line, "\n", 2)
		if len(parts) == 2 {
			c.line = parts[1]
		} else {
			c.line = ""
		}
		if c.state == DATA {
			c.message.Data += parts[0] + "\n"
			if(strings.HasSuffix(c.message.Data, "\r\n.\r\n")) {
				c.state = DONE
				c.Write("250", "Ok: queued as nnnnnnnn")
			}
		} else {
			c.Process(strings.Trim(parts[0], "\r\n"))
		}
	}

	c.Read()
}

func (c *Session) Write(code string, text ...string) {
	if len(text) == 1 {
		c.conn.Write([]byte(code + " " + text[0] + "\n"))
		return
	}
	for i := 0; i < len(text) - 2; i++ {
		c.conn.Write([]byte(code + "-" + text[i] + "\n"))
	}
	c.conn.Write([]byte(code + " " + text[len(text)] + "\n"))
}

func (c *Session) Process(line string) {
	c.log("Processing line: %s", line)

	words := strings.Split(line, " ")
	command := words[0]
	args := strings.Join(words[1:len(words)], " ")
	c.log("In state %d, got command '%s', args '%s'", c.state, command, args)

	switch {
		case command == "RSET":
			c.log("Got RSET command, switching to ESTABLISH state")
			c.state = ESTABLISH
			c.message = &Message{}
			c.Write("250", "Ok")
		case command == "NOOP":
			c.log("Got NOOP command")
			c.Write("250", "Ok")
		case command == "QUIT":
			c.log("Got QUIT command")
			c.Write("221", "Bye")
		case c.state == ESTABLISH:
			switch command {
				case "HELO":
					c.log("Got HELO command, switching to MAIL state")
					c.state = MAIL
					c.message.Helo = args
					c.Write("250", "Hello " + args)
				case "EHLO":
					c.log("Got EHLO command, switching to MAIL state")
					c.state = MAIL
					c.message.Helo = args
					c.Write("250", "Hello " + args)
				default:
					c.log("Got unknown command for ESTABLISH state: '%s'", command)
			}
		case c.state == MAIL:
			switch command {
				case "MAIL":
					c.log("Got MAIL command, switching to RCPT state")
					// TODO parse args
					c.message.From = args
					c.state = RCPT
					c.Write("250", "Ok")
				default:
					c.log("Got unknown command for MAIL state: '%s'", command)
			}
		case c.state == RCPT:
			switch command {
				case "RCPT":
					c.log("Got RCPT command")
					c.message.To = append(c.message.To, args)
					c.Write("250", "Ok")
				case "DATA":
					c.log("Got DATA command, switching to DATA state")
					c.state = DATA
					c.Write("354", "End data with <CR><LF>.<CR><LF>")
				default:
					c.log("Got unknown command for RCPT state: '%s'", command)
			}
		case c.state == DONE:
			switch command {
				case "MAIL":
					c.log("Got MAIL command")
					// TODO parse args
					c.message.From = args
					c.state = RCPT
					c.Write("250", "Ok")
				default:
					c.log("Got unknown command for DONE state: '%s'", command)
			}
	}
}
<|endoftext|>


<commit_before>package watch

import (
	"fmt"
	"log"
	"os"
	"reflect"
	"time"

	"github.com/armon/consul-api"
)

const (
	// retryInterval is the base retry value
	retryInterval = 5 * time.Second

	// maximum back off time, this is to prevent
	// exponential runaway
	maxBackoffTime = 180 * time.Second
)

// Run is used to run a watch plan
func (p *WatchPlan) Run(address string) error {
	// Setup the client
	p.address = address
	conf := consulapi.DefaultConfig()
	conf.Address = address
	conf.Datacenter = p.Datacenter
	conf.Token = p.Token
	client, err := consulapi.NewClient(conf)
	if err != nil {
		return fmt.Errorf("Failed to connect to agent: %v", err)
	}
	p.client = client

	// Create the logger
	output := p.LogOutput
	if output == nil {
		output = os.Stderr
	}
	logger := log.New(output, "", log.LstdFlags)

	// Loop until we are canceled
	failures := 0
OUTER:
	for !p.shouldStop() {
		// Invoke the handler
		index, result, err := p.Func(p)

		// Check if we should terminate since the function
		// could have blocked for a while
		if p.shouldStop() {
			break
		}

		// Handle an error in the watch function
		if err != nil {
			// Perform an exponential backoff
			failures++
			retry := retryInterval * time.Duration(failures*failures)
			if retry > maxBackoffTime {
				retry = maxBackoffTime
			}
			logger.Printf("consul.watch: Watch (type: %s) errored: %v, retry in %v",
				p.Type, err, retry)
			select {
			case <-time.After(retry):
				continue OUTER
			case <-p.stopCh:
				return nil
			}
		}

		// Clear the failures
		failures = 0

		// If the index is unchanged do nothing
		if index == p.lastIndex {
			continue
		}

		// Update the index, look for change
		p.lastIndex = index
		if reflect.DeepEqual(p.lastResult, result) {
			continue
		}

		// Handle the updated result
		p.lastResult = result
		if p.Handler != nil {
			p.Handler(index, result)
		}
	}
	return nil
}

// Stop is used to stop running the watch plan
func (p *WatchPlan) Stop() {
	p.stopLock.Lock()
	defer p.stopLock.Unlock()
	if p.stop {
		return
	}
	p.stop = true
	close(p.stopCh)
}

func (p *WatchPlan) shouldStop() bool {
	select {
	case <-p.stopCh:
		return true
	default:
		return false
	}
}
<commit_msg>watch: Fixing bug with null keys<commit_after>package watch

import (
	"fmt"
	"log"
	"os"
	"reflect"
	"time"

	"github.com/armon/consul-api"
)

const (
	// retryInterval is the base retry value
	retryInterval = 5 * time.Second

	// maximum back off time, this is to prevent
	// exponential runaway
	maxBackoffTime = 180 * time.Second
)

// Run is used to run a watch plan
func (p *WatchPlan) Run(address string) error {
	// Setup the client
	p.address = address
	conf := consulapi.DefaultConfig()
	conf.Address = address
	conf.Datacenter = p.Datacenter
	conf.Token = p.Token
	client, err := consulapi.NewClient(conf)
	if err != nil {
		return fmt.Errorf("Failed to connect to agent: %v", err)
	}
	p.client = client

	// Create the logger
	output := p.LogOutput
	if output == nil {
		output = os.Stderr
	}
	logger := log.New(output, "", log.LstdFlags)

	// Loop until we are canceled
	failures := 0
OUTER:
	for !p.shouldStop() {
		// Invoke the handler
		index, result, err := p.Func(p)

		// Check if we should terminate since the function
		// could have blocked for a while
		if p.shouldStop() {
			break
		}

		// Handle an error in the watch function
		if err != nil {
			// Perform an exponential backoff
			failures++
			retry := retryInterval * time.Duration(failures*failures)
			if retry > maxBackoffTime {
				retry = maxBackoffTime
			}
			logger.Printf("consul.watch: Watch (type: %s) errored: %v, retry in %v",
				p.Type, err, retry)
			select {
			case <-time.After(retry):
				continue OUTER
			case <-p.stopCh:
				return nil
			}
		}

		// Clear the failures
		failures = 0

		// If the index is unchanged do nothing
		if index == p.lastIndex {
			continue
		}

		// Update the index, look for change
		oldIndex := p.lastIndex
		p.lastIndex = index
		if oldIndex != 0 && reflect.DeepEqual(p.lastResult, result) {
			continue
		}

		// Handle the updated result
		p.lastResult = result
		if p.Handler != nil {
			p.Handler(index, result)
		}
	}
	return nil
}

// Stop is used to stop running the watch plan
func (p *WatchPlan) Stop() {
	p.stopLock.Lock()
	defer p.stopLock.Unlock()
	if p.stop {
		return
	}
	p.stop = true
	close(p.stopCh)
}

func (p *WatchPlan) shouldStop() bool {
	select {
	case <-p.stopCh:
		return true
	default:
		return false
	}
}
<|endoftext|>


<commit_before>package math

func IntMin(a, b int) int {
	if a < b {
		return a
	}
	return b
}

func IntMax(a, b int) int {
	if a < b {
		return b
	}
	return a
}

func UintMin(a, b uint) uint {
	if a < b {
		return a
	}
	return b
}

func UintMax(a, b uint) uint {
	if a < b {
		return b
	}
	return a
}
<commit_msg>math: comments<commit_after>package math

// pkg math implements some common util functions

// IntMin returns the smaller of x or y.
func IntMin(x, y int) int {
	if x < y {
		return x
	}
	return y
}

// IntMax returns the larger of x or y.
func IntMax(x, y int) int {
	if x < y {
		return y
	}
	return x
}

// UintMin returns the smaller of x or y.
func UintMin(x, y uint) uint {
	if x < y {
		return x
	}
	return y
}

// UintMax returns the larger of x or y.
func UintMax(x, y uint) uint {
	if x < y {
		return y
	}
	return x
}
<|endoftext|>


<commit_before>// errorcheck

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

// Issue 11674: cmd/compile: does not diagnose constant division by
// zero

package p

const x complex64 = 0
const y complex128 = 0

var _ = x / 1e-20
var _ = x / 1e-50   // ERROR "complex division by zero"
var _ = x / 1e-1000 // ERROR "complex division by zero"
var _ = x / 1e-20i
var _ = x / 1e-50i   // ERROR "complex division by zero"
var _ = x / 1e-1000i // ERROR "complex division by zero"

var _ = x / 1e-45 // smallest positive float32

var _ = x / (1e-20 + 1e-20i)
var _ = x / (1e-50 + 1e-20i)
var _ = x / (1e-20 + 1e-50i)
var _ = x / (1e-50 + 1e-50i)     // ERROR "complex division by zero"
var _ = x / (1e-1000 + 1e-1000i) // ERROR "complex division by zero"

var _ = y / 1e-50
var _ = y / 1e-1000 // ERROR "complex division by zero"
var _ = y / 1e-50i
var _ = y / 1e-1000i // ERROR "complex division by zero"

var _ = y / 5e-324 // smallest positive float64

var _ = y / (1e-50 + 1e-50)
var _ = y / (1e-1000 + 1e-50i)
var _ = y / (1e-50 + 1e-1000i)
var _ = y / (1e-1000 + 1e-1000i) // ERROR "complex division by zero"
<commit_msg>test: only require issue11674 errors with gc compiler<commit_after>// errorcheck

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

// Issue 11674: cmd/compile: does not diagnose constant division by
// zero

package p

const x complex64 = 0
const y complex128 = 0

var _ = x / 1e-20
var _ = x / 1e-50   // GC_ERROR "complex division by zero"
var _ = x / 1e-1000 // GC_ERROR "complex division by zero"
var _ = x / 1e-20i
var _ = x / 1e-50i   // GC_ERROR "complex division by zero"
var _ = x / 1e-1000i // GC_ERROR "complex division by zero"

var _ = x / 1e-45 // smallest positive float32

var _ = x / (1e-20 + 1e-20i)
var _ = x / (1e-50 + 1e-20i)
var _ = x / (1e-20 + 1e-50i)
var _ = x / (1e-50 + 1e-50i)     // GC_ERROR "complex division by zero"
var _ = x / (1e-1000 + 1e-1000i) // GC_ERROR "complex division by zero"

var _ = y / 1e-50
var _ = y / 1e-1000 // GC_ERROR "complex division by zero"
var _ = y / 1e-50i
var _ = y / 1e-1000i // GC_ERROR "complex division by zero"

var _ = y / 5e-324 // smallest positive float64

var _ = y / (1e-50 + 1e-50)
var _ = y / (1e-1000 + 1e-50i)
var _ = y / (1e-50 + 1e-1000i)
var _ = y / (1e-1000 + 1e-1000i) // GC_ERROR "complex division by zero"
<|endoftext|>


<commit_before>// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can
// be found in the LICENSE file.

package termui

import (
	"fmt"
)

// This is the implemetation of multi-colored or stacked bar graph.  This is different from default barGraph which is implemented in bar.go
// Multi-Colored-BarChart creates multiple bars in a widget:
/*
   bc := termui.NewMBarChart()
   data := make([][]int, 2)
   data[0] := []int{3, 2, 5, 7, 9, 4}
   data[1] := []int{7, 8, 5, 3, 1, 6}
   bclabels := []string{"S0", "S1", "S2", "S3", "S4", "S5"}
   bc.BorderLabel = "Bar Chart"
   bc.Data = data
   bc.Width = 26
   bc.Height = 10
   bc.DataLabels = bclabels
   bc.TextColor = termui.ColorGreen
   bc.BarColor = termui.ColorRed
   bc.NumColor = termui.ColorYellow
*/
type MBarChart struct {
	Block
	BarColor   [NumberofColors]Attribute
	TextColor  Attribute
	NumColor   [NumberofColors]Attribute
	Data       [NumberofColors][]int
	DataLabels []string
	BarWidth   int
	BarGap     int
	labels     [][]rune
	dataNum    [NumberofColors][][]rune
	numBar     int
	scale      float64
	max        int
	minDataLen int
	numStack   int
	ShowScale  bool
	maxScale   []rune
}

// NewBarChart returns a new *BarChart with current theme.
func NewMBarChart() *MBarChart {
	bc := &MBarChart{Block: *NewBlock()}
	bc.BarColor[0] = ThemeAttr("mbarchart.bar.bg")
	bc.NumColor[0] = ThemeAttr("mbarchart.num.fg")
	bc.TextColor = ThemeAttr("mbarchart.text.fg")
	bc.BarGap = 1
	bc.BarWidth = 3
	return bc
}

func (bc *MBarChart) layout() {
	bc.numBar = bc.innerArea.Dx() / (bc.BarGap + bc.BarWidth)
	bc.labels = make([][]rune, bc.numBar)
	DataLen := 0
	LabelLen := len(bc.DataLabels)
	bc.minDataLen = 9999 //Set this to some very hight value so that we find the minimum one We want to know which array among data[][] has got the least length

	// We need to know how many stack/data array data[0] , data[1] are there
	for i := 0; i < len(bc.Data); i++ {
		if bc.Data[i] == nil {
			break
		}
		DataLen++
	}
	bc.numStack = DataLen

	//We need to know what is the mimimum size of data array data[0] could have 10 elements data[1] could have only 5, so we plot only 5 bar graphs

	for i := 0; i < DataLen; i++ {
		if bc.minDataLen > len(bc.Data[i]) {
			bc.minDataLen = len(bc.Data[i])
		}
	}

	if LabelLen > bc.minDataLen {
		LabelLen = bc.minDataLen
	}

	for i := 0; i < LabelLen && i < bc.numBar; i++ {
		bc.labels[i] = trimStr2Runes(bc.DataLabels[i], bc.BarWidth)
	}

	for i := 0; i < bc.numStack; i++ {
		bc.dataNum[i] = make([][]rune, len(bc.Data[i]))
		//For each stack of bar calculate the rune
		for j := 0; j < LabelLen && i < bc.numBar; j++ {
			n := bc.Data[i][j]
			s := fmt.Sprint(n)
			bc.dataNum[i][j] = trimStr2Runes(s, bc.BarWidth)
		}
		//If color is not defined by default then populate a color that is different from the prevous bar
		if bc.BarColor[i] == ColorDefault && bc.NumColor[i] == ColorDefault {
			if i == 0 {
				bc.BarColor[i] = ColorBlack
			} else {
				bc.BarColor[i] = bc.BarColor[i-1] + 1
				if bc.BarColor[i] > NumberofColors {
					bc.BarColor[i] = ColorBlack
				}
			}
			bc.NumColor[i] = (NumberofColors + 1) - bc.BarColor[i] //Make NumColor opposite of barColor for visibility
		}
	}

	//If Max value is not set then we have to populate, this time the max value will be max(sum(d1[0],d2[0],d3[0]) .... sum(d1[n], d2[n], d3[n]))

	if bc.max == 0 {
		bc.max = -1
	}
	for i := 0; i < bc.minDataLen && i < LabelLen; i++ {
		var dsum int
		for j := 0; j < bc.numStack; j++ {
			dsum += bc.Data[j][i]
		}
		if dsum > bc.max {
			bc.max = dsum
		}
	}

	//Finally Calculate max sale
	if bc.ShowScale {
		s := fmt.Sprintf("%d", bc.max)
		bc.maxScale = trimStr2Runes(s, len(s))
		bc.scale = float64(bc.max) / float64(bc.innerArea.Dy()-2)
	} else {
		bc.scale = float64(bc.max) / float64(bc.innerArea.Dy()-1)
	}

}

func (bc *MBarChart) SetMax(max int) {

	if max > 0 {
		bc.max = max
	}
}

// Buffer implements Bufferer interface.
func (bc *MBarChart) Buffer() Buffer {
	buf := bc.Block.Buffer()
	bc.layout()
	var oftX int

	for i := 0; i < bc.numBar && i < bc.minDataLen && i < len(bc.DataLabels); i++ {
		ph := 0 //Previous Height to stack up
		oftX = i * (bc.BarWidth + bc.BarGap)
		for i1 := 0; i1 < bc.numStack; i1++ {
			h := int(float64(bc.Data[i1][i]) / bc.scale)
			// plot bars
			for j := 0; j < bc.BarWidth; j++ {
				for k := 0; k < h; k++ {
					c := Cell{
						Ch: ' ',
						Bg: bc.BarColor[i1],
					}
					if bc.BarColor[i1] == ColorDefault { // when color is default, space char treated as transparent!
						c.Bg |= AttrReverse
					}
					x := bc.innerArea.Min.X + i*(bc.BarWidth+bc.BarGap) + j
					y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 2 - k - ph
					buf.Set(x, y, c)

				}
			}
			ph += h
		}
		// plot text
		for j, k := 0, 0; j < len(bc.labels[i]); j++ {
			w := charWidth(bc.labels[i][j])
			c := Cell{
				Ch: bc.labels[i][j],
				Bg: bc.Bg,
				Fg: bc.TextColor,
			}
			y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 1
			x := bc.innerArea.Max.X + oftX + ((bc.BarWidth - len(bc.labels[i])) / 2) + k
			buf.Set(x, y, c)
			k += w
		}
		// plot num
		ph = 0 //re-initialize previous height
		for i1 := 0; i1 < bc.numStack; i1++ {
			h := int(float64(bc.Data[i1][i]) / bc.scale)
			for j := 0; j < len(bc.dataNum[i1][i]) && h > 0; j++ {
				c := Cell{
					Ch: bc.dataNum[i1][i][j],
					Fg: bc.NumColor[i1],
					Bg: bc.BarColor[i1],
				}
				if bc.BarColor[i1] == ColorDefault { // the same as above
					c.Bg |= AttrReverse
				}
				if h == 0 {
					c.Bg = bc.Bg
				}
				x := bc.innerArea.Min.X + oftX + (bc.BarWidth-len(bc.dataNum[i1][i]))/2 + j
				y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 2 - ph
				buf.Set(x, y, c)
			}
			ph += h
		}
	}

	if bc.ShowScale {
		//Currently bar graph only supprts data range from 0 to MAX
		//Plot 0
		c := Cell{
			Ch: '0',
			Bg: bc.Bg,
			Fg: bc.TextColor,
		}

		y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 2
		x := bc.X
		buf.Set(x, y, c)

		//Plot the maximum sacle value
		for i := 0; i < len(bc.maxScale); i++ {
			c := Cell{
				Ch: bc.maxScale[i],
				Bg: bc.Bg,
				Fg: bc.TextColor,
			}

			y := bc.innerArea.Min.Y
			x := bc.X + i

			buf.Set(x, y, c)
		}

	}

	return buf
}
<commit_msg>spelling: implementation<commit_after>// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can
// be found in the LICENSE file.

package termui

import (
	"fmt"
)

// This is the implementation of multi-colored or stacked bar graph.  This is different from default barGraph which is implemented in bar.go
// Multi-Colored-BarChart creates multiple bars in a widget:
/*
   bc := termui.NewMBarChart()
   data := make([][]int, 2)
   data[0] := []int{3, 2, 5, 7, 9, 4}
   data[1] := []int{7, 8, 5, 3, 1, 6}
   bclabels := []string{"S0", "S1", "S2", "S3", "S4", "S5"}
   bc.BorderLabel = "Bar Chart"
   bc.Data = data
   bc.Width = 26
   bc.Height = 10
   bc.DataLabels = bclabels
   bc.TextColor = termui.ColorGreen
   bc.BarColor = termui.ColorRed
   bc.NumColor = termui.ColorYellow
*/
type MBarChart struct {
	Block
	BarColor   [NumberofColors]Attribute
	TextColor  Attribute
	NumColor   [NumberofColors]Attribute
	Data       [NumberofColors][]int
	DataLabels []string
	BarWidth   int
	BarGap     int
	labels     [][]rune
	dataNum    [NumberofColors][][]rune
	numBar     int
	scale      float64
	max        int
	minDataLen int
	numStack   int
	ShowScale  bool
	maxScale   []rune
}

// NewBarChart returns a new *BarChart with current theme.
func NewMBarChart() *MBarChart {
	bc := &MBarChart{Block: *NewBlock()}
	bc.BarColor[0] = ThemeAttr("mbarchart.bar.bg")
	bc.NumColor[0] = ThemeAttr("mbarchart.num.fg")
	bc.TextColor = ThemeAttr("mbarchart.text.fg")
	bc.BarGap = 1
	bc.BarWidth = 3
	return bc
}

func (bc *MBarChart) layout() {
	bc.numBar = bc.innerArea.Dx() / (bc.BarGap + bc.BarWidth)
	bc.labels = make([][]rune, bc.numBar)
	DataLen := 0
	LabelLen := len(bc.DataLabels)
	bc.minDataLen = 9999 //Set this to some very hight value so that we find the minimum one We want to know which array among data[][] has got the least length

	// We need to know how many stack/data array data[0] , data[1] are there
	for i := 0; i < len(bc.Data); i++ {
		if bc.Data[i] == nil {
			break
		}
		DataLen++
	}
	bc.numStack = DataLen

	//We need to know what is the mimimum size of data array data[0] could have 10 elements data[1] could have only 5, so we plot only 5 bar graphs

	for i := 0; i < DataLen; i++ {
		if bc.minDataLen > len(bc.Data[i]) {
			bc.minDataLen = len(bc.Data[i])
		}
	}

	if LabelLen > bc.minDataLen {
		LabelLen = bc.minDataLen
	}

	for i := 0; i < LabelLen && i < bc.numBar; i++ {
		bc.labels[i] = trimStr2Runes(bc.DataLabels[i], bc.BarWidth)
	}

	for i := 0; i < bc.numStack; i++ {
		bc.dataNum[i] = make([][]rune, len(bc.Data[i]))
		//For each stack of bar calculate the rune
		for j := 0; j < LabelLen && i < bc.numBar; j++ {
			n := bc.Data[i][j]
			s := fmt.Sprint(n)
			bc.dataNum[i][j] = trimStr2Runes(s, bc.BarWidth)
		}
		//If color is not defined by default then populate a color that is different from the prevous bar
		if bc.BarColor[i] == ColorDefault && bc.NumColor[i] == ColorDefault {
			if i == 0 {
				bc.BarColor[i] = ColorBlack
			} else {
				bc.BarColor[i] = bc.BarColor[i-1] + 1
				if bc.BarColor[i] > NumberofColors {
					bc.BarColor[i] = ColorBlack
				}
			}
			bc.NumColor[i] = (NumberofColors + 1) - bc.BarColor[i] //Make NumColor opposite of barColor for visibility
		}
	}

	//If Max value is not set then we have to populate, this time the max value will be max(sum(d1[0],d2[0],d3[0]) .... sum(d1[n], d2[n], d3[n]))

	if bc.max == 0 {
		bc.max = -1
	}
	for i := 0; i < bc.minDataLen && i < LabelLen; i++ {
		var dsum int
		for j := 0; j < bc.numStack; j++ {
			dsum += bc.Data[j][i]
		}
		if dsum > bc.max {
			bc.max = dsum
		}
	}

	//Finally Calculate max sale
	if bc.ShowScale {
		s := fmt.Sprintf("%d", bc.max)
		bc.maxScale = trimStr2Runes(s, len(s))
		bc.scale = float64(bc.max) / float64(bc.innerArea.Dy()-2)
	} else {
		bc.scale = float64(bc.max) / float64(bc.innerArea.Dy()-1)
	}

}

func (bc *MBarChart) SetMax(max int) {

	if max > 0 {
		bc.max = max
	}
}

// Buffer implements Bufferer interface.
func (bc *MBarChart) Buffer() Buffer {
	buf := bc.Block.Buffer()
	bc.layout()
	var oftX int

	for i := 0; i < bc.numBar && i < bc.minDataLen && i < len(bc.DataLabels); i++ {
		ph := 0 //Previous Height to stack up
		oftX = i * (bc.BarWidth + bc.BarGap)
		for i1 := 0; i1 < bc.numStack; i1++ {
			h := int(float64(bc.Data[i1][i]) / bc.scale)
			// plot bars
			for j := 0; j < bc.BarWidth; j++ {
				for k := 0; k < h; k++ {
					c := Cell{
						Ch: ' ',
						Bg: bc.BarColor[i1],
					}
					if bc.BarColor[i1] == ColorDefault { // when color is default, space char treated as transparent!
						c.Bg |= AttrReverse
					}
					x := bc.innerArea.Min.X + i*(bc.BarWidth+bc.BarGap) + j
					y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 2 - k - ph
					buf.Set(x, y, c)

				}
			}
			ph += h
		}
		// plot text
		for j, k := 0, 0; j < len(bc.labels[i]); j++ {
			w := charWidth(bc.labels[i][j])
			c := Cell{
				Ch: bc.labels[i][j],
				Bg: bc.Bg,
				Fg: bc.TextColor,
			}
			y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 1
			x := bc.innerArea.Max.X + oftX + ((bc.BarWidth - len(bc.labels[i])) / 2) + k
			buf.Set(x, y, c)
			k += w
		}
		// plot num
		ph = 0 //re-initialize previous height
		for i1 := 0; i1 < bc.numStack; i1++ {
			h := int(float64(bc.Data[i1][i]) / bc.scale)
			for j := 0; j < len(bc.dataNum[i1][i]) && h > 0; j++ {
				c := Cell{
					Ch: bc.dataNum[i1][i][j],
					Fg: bc.NumColor[i1],
					Bg: bc.BarColor[i1],
				}
				if bc.BarColor[i1] == ColorDefault { // the same as above
					c.Bg |= AttrReverse
				}
				if h == 0 {
					c.Bg = bc.Bg
				}
				x := bc.innerArea.Min.X + oftX + (bc.BarWidth-len(bc.dataNum[i1][i]))/2 + j
				y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 2 - ph
				buf.Set(x, y, c)
			}
			ph += h
		}
	}

	if bc.ShowScale {
		//Currently bar graph only supprts data range from 0 to MAX
		//Plot 0
		c := Cell{
			Ch: '0',
			Bg: bc.Bg,
			Fg: bc.TextColor,
		}

		y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 2
		x := bc.X
		buf.Set(x, y, c)

		//Plot the maximum sacle value
		for i := 0; i < len(bc.maxScale); i++ {
			c := Cell{
				Ch: bc.maxScale[i],
				Bg: bc.Bg,
				Fg: bc.TextColor,
			}

			y := bc.innerArea.Min.Y
			x := bc.X + i

			buf.Set(x, y, c)
		}

	}

	return buf
}
<|endoftext|>