<commit_before>package MQTTg

import (
	"strings"
)

type TopicNode struct {
	Nodes         map[string]*TopicNode
	FullPath      string
	RetainMessage string
	RetainQoS     uint8
	Subscribers   map[string]uint8 // map[clientID]QoS
}

func (self *TopicNode) GetNodesByNumberSign() (out []*TopicNode) {
	out = []*TopicNode{self}
	if len(self.Nodes) > 0 {
		for key, node := range self.Nodes {
			if strings.HasPrefix(key, "$") {
				continue
			}
			out = append(out, node.GetNodesByNumberSign()...)
		}
	}
	return out
}

func (self *TopicNode) GetTopicNodes(topic string) (out []*TopicNode, e error) {
	// this topic may have wildcard +*
	parts := strings.Split(topic, "/")
	nxt := self
	ok := false
	currentPath := ""
	for i, part := range parts {
		bef := nxt
		currentPath += part
		if i != len(parts)-1 {
			currentPath += "/"
		}
		switch part {
		case "+":
			// e.g.) A/+/C/D
			for key, _ := range bef.Nodes {
				if strings.HasPrefix(key, "$") {
					continue
				}
				tmp, err := self.GetTopicNodes(strings.Replace(topic, "+", key, 1))
				if err != nil {
					return nil, err
				}
				out = append(out, tmp...)
			}
			return out, nil
		case "#":
			if i != len(parts)-1 {
				return nil, MULTI_LEVEL_WILDCARD_MUST_BE_ON_TAIL
			}
			out = append(out, bef.GetNodesByNumberSign()...)
		default:
			if strings.HasSuffix(part, "#") && strings.HasSuffix(part, "+") {
				return nil, WILDCARD_MUST_NOT_BE_ADJACENT_TO_NAME
			}
			nxt, ok = bef.Nodes[part]
			if !ok {
				bef.ApplyNewTopic(part, currentPath)
				nxt, _ = bef.Nodes[part]
			}
			if len(parts)-1 == i {
				out = append(out, nxt)
			}
		}
	}
	return out, nil
}

func (self *TopicNode) ApplySubscriber(clientID, topic string, qos uint8) ([]*TopicNode, []SubscribeReturnCode, error) {
	// find topic edge and apply the clientID
	edges, err := self.GetTopicNodes(topic)
	if err != nil {
		return nil, []SubscribeReturnCode{SubscribeFailure}, err
	}
	codes := make([]SubscribeReturnCode, len(edges))
	for i, edge := range edges {
		// TODO: the return code should be managed by broker
		edge.Subscribers[clientID] = qos
		codes[i] = SubscribeReturnCode(qos)
	}
	return edges, codes, nil
}

func (self *TopicNode) DeleteSubscriber(clientID, topic string) error {
	edges, err := self.GetTopicNodes(topic)
	if err != nil {
		return err
	}
	for _, edge := range edges {
		delete(edge.Subscribers, clientID)
	}
	return nil
}

func (self *TopicNode) ApplyRetain(topic string, qos uint8, retain string) error {
	edges, err := self.GetTopicNodes(topic)
	if err != nil {
		return err
	}
	for _, edge := range edges {
		// for debug to store all retain mesage
		// edge.RetainMessage = append(edge.RetainMessage, retain)
		edge.RetainMessage = retain
		edge.RetainQoS = qos
	}

	return nil
}

func (self *TopicNode) ApplyNewTopic(topic, fullPath string) {
	self.Nodes[topic] = &TopicNode{
		Nodes:         make(map[string]*TopicNode),
		FullPath:      fullPath,
		RetainMessage: "",
		RetainQoS:     0,
		Subscribers:   make(map[string]uint8),
	}
}

func (self *TopicNode) DumpTree() (str string) {
	if len(self.Nodes) == 0 {
		return self.FullPath + "\n"
	}
	for _, v := range self.Nodes {
		str += v.DumpTree()
	}
	return str

}
<commit_msg>fix bug when number sign is used<commit_after>package MQTTg

import (
	"strings"
)

type TopicNode struct {
	Nodes         map[string]*TopicNode
	FullPath      string
	RetainMessage string
	RetainQoS     uint8
	Subscribers   map[string]uint8 // map[clientID]QoS
}

func (self *TopicNode) GetNodesByNumberSign() (out []*TopicNode) {
	self.FullPath = strings.TrimSuffix(self.FullPath, "/")
	out = []*TopicNode{self}
	if len(self.Nodes) > 0 {
		for key, node := range self.Nodes {
			if strings.HasPrefix(key, "$") {
				continue
			}
			out = append(out, node.GetNodesByNumberSign()...)
		}
	}
	return out
}

func (self *TopicNode) GetTopicNodes(topic string) (out []*TopicNode, e error) {
	// this topic may have wildcard +*
	parts := strings.Split(topic, "/")
	nxt := self
	ok := false
	currentPath := ""
	for i, part := range parts {
		bef := nxt
		currentPath += part
		if i != len(parts)-1 {
			currentPath += "/"
		}
		switch part {
		case "+":
			// e.g.) A/+/C/D
			for key, _ := range bef.Nodes {
				if strings.HasPrefix(key, "$") {
					continue
				}
				tmp, err := self.GetTopicNodes(strings.Replace(topic, "+", key, 1))
				if err != nil {
					return nil, err
				}
				out = append(out, tmp...)
			}
			return out, nil
		case "#":
			if i != len(parts)-1 {
				return nil, MULTI_LEVEL_WILDCARD_MUST_BE_ON_TAIL
			}
			out = append(out, bef.GetNodesByNumberSign()...)
		default:
			if strings.HasSuffix(part, "#") && strings.HasSuffix(part, "+") {
				return nil, WILDCARD_MUST_NOT_BE_ADJACENT_TO_NAME
			}
			nxt, ok = bef.Nodes[part]
			if !ok {
				bef.ApplyNewTopic(part, currentPath)
				nxt, _ = bef.Nodes[part]
			}
			if len(parts)-1 == i {
				out = append(out, nxt)
			}
		}
	}
	return out, nil
}

func (self *TopicNode) ApplySubscriber(clientID, topic string, qos uint8) ([]*TopicNode, []SubscribeReturnCode, error) {
	// find topic edge and apply the clientID
	edges, err := self.GetTopicNodes(topic)
	if err != nil {
		return nil, []SubscribeReturnCode{SubscribeFailure}, err
	}
	codes := make([]SubscribeReturnCode, len(edges))
	for i, edge := range edges {
		// TODO: the return code should be managed by broker
		edge.Subscribers[clientID] = qos
		codes[i] = SubscribeReturnCode(qos)
	}
	return edges, codes, nil
}

func (self *TopicNode) DeleteSubscriber(clientID, topic string) error {
	edges, err := self.GetTopicNodes(topic)
	if err != nil {
		return err
	}
	for _, edge := range edges {
		delete(edge.Subscribers, clientID)
	}
	return nil
}

func (self *TopicNode) ApplyRetain(topic string, qos uint8, retain string) error {
	edges, err := self.GetTopicNodes(topic)
	if err != nil {
		return err
	}
	for _, edge := range edges {
		// for debug to store all retain mesage
		// edge.RetainMessage = append(edge.RetainMessage, retain)
		edge.RetainMessage = retain
		edge.RetainQoS = qos
	}

	return nil
}

func (self *TopicNode) ApplyNewTopic(topic, fullPath string) {
	self.Nodes[topic] = &TopicNode{
		Nodes:         make(map[string]*TopicNode),
		FullPath:      fullPath,
		RetainMessage: "",
		RetainQoS:     0,
		Subscribers:   make(map[string]uint8),
	}
}

func (self *TopicNode) DumpTree() (str string) {
	if len(self.Nodes) == 0 {
		return self.FullPath + "\n"
	}
	for _, v := range self.Nodes {
		str += v.DumpTree()
	}
	return str

}
<|endoftext|>


<commit_before>package protecter

import (
	"net/http"

	"github.com/herb-go/herb/user/credential"
)

type Credentialer interface {
	Credential(r *http.Request) credential.Credential
}

type CredentialerFactory interface {
	CreateCredentialer(func(v interface{}) error) (Credentialer, error)
}

type Credential struct {
	Request *http.Request
	Loader  *CredentialLoader
}

func (c *Credential) Type() credential.Type {
	return c.Loader.CredentialType
}
func (c *Credential) Data() ([]byte, error) {
	return c.Loader.LoaderFunc(c.Request)
}

type CredentialLoader struct {
	CredentialType credential.Type
	LoaderFunc     func(*http.Request) ([]byte, error)
}

func (c *CredentialLoader) Credential(r *http.Request) credential.Credential {
	return &Credential{
		Request: r,
		Loader:  c,
	}
}
<commit_msg>update<commit_after>package protecter

import (
	"net/http"

	"github.com/herb-go/herb/user/credential"
)

type Credentialer interface {
	Credential(r *http.Request) credential.Credential
}

type Credential struct {
	Request *http.Request
	Loader  *CredentialLoader
}

func (c *Credential) Type() credential.Type {
	return c.Loader.CredentialType
}
func (c *Credential) Data() ([]byte, error) {
	return c.Loader.LoaderFunc(c.Request)
}

type CredentialLoader struct {
	CredentialType credential.Type
	LoaderFunc     func(*http.Request) ([]byte, error)
}

func (c *CredentialLoader) Credential(r *http.Request) credential.Credential {
	return &Credential{
		Request: r,
		Loader:  c,
	}
}
<|endoftext|>


<commit_before>// +build windows

package simple

import (
	"fmt"
	"strings"
	"syscall"
	"time"

	"log"
	"golang.org/x/sys/windows"
	"github.com/google/winops/winlog/wevtapi"
	"github.com/google/winops/winlog"
)

// WindowsEvent implements Event interface to subscribe events from Windows Event Log.
type WindowsEvent struct {
	config       *winlog.SubscribeConfig
	subscription windows.Handle
}

// NewWindowsEvent creates a WindowsEvent object.
func NewWindowsEvent() Event {
	return &WindowsEvent{}
}

// Subscribe creates the bookmark from XML formatted string and initializes a subscription for
// Windows Event Log.
func (e *WindowsEvent) Subscribe(bookmark string, query map[string]string) error {
	if e.config != nil || e.subscription != 0 {
		return fmt.Errorf("double subscribed Windows Event for: %+v", e)
	}

	var err error
	e.config, err = winlog.DefaultSubscribeConfig()
	if err != nil {
		return fmt.Errorf("winlog.DefaultSubscribeConfig failed: %w", err)
	}
	if bookmark == "" {
		e.config.Bookmark, err = wevtapi.EvtCreateBookmark(nil)
		if err != nil {
			return fmt.Errorf("wevtapi.EvtCreateBookmark failed: %w", err)
		}
	} else {
		e.config.Bookmark, err = wevtapi.EvtCreateBookmark(syscall.StringToUTF16Ptr(bookmark))
		if err != nil {
			log.Warningf("Create a new bookmark because the existing bookmark might be corrupted: %s", bookmark)
			e.config.Bookmark, err = wevtapi.EvtCreateBookmark(nil)
			if err != nil {
				return fmt.Errorf("wevtapi.EvtCreateBookmark failed: %w", err)
			}
		}
	}

	if len(query) != 0 {
		channels, err := winlog.AvailableChannels()
		if err != nil {
			return fmt.Errorf("finding available channels: %w", err)
		}
		channelSet := make(map[string]bool)
		for _, c := range channels {
			channelSet[strings.ToLower(c)] = true
		}
		// Filter out channels that are not available
		q := make(map[string]string)
		for k, v := range query {
			if channelSet[strings.ToLower(k)] {
				q[k] = v
			} else {
				log.Warningf("Ignoring non-existent Windows Event Log channel %q", k)
			}
		}
		xmlQuery, err := winlog.BuildStructuredXMLQuery(q)
		if err != nil {
			return fmt.Errorf("Build structured XML query error: %w", err)
		}
		log.V(1).Infof("Built the structured XML Query: %s", xmlQuery)
		e.config.Query, err = syscall.UTF16PtrFromString(string(xmlQuery))
		if err != nil {
			return fmt.Errorf("syscall.UTF16PtrFromString failed: %w", err)
		}
	}

	e.config.Flags = wevtapi.EvtSubscribeStartAfterBookmark
	e.subscription, err = winlog.Subscribe(e.config)
	return err
}

// WaitForSingleObject waits for new events to arrive. Returns true if the event
// was signalled before the timeout expired. Timeout must be between 0 and 2^32us.
func (e *WindowsEvent) WaitForSingleObject(timeout time.Duration) (bool, error) {
	status, err := windows.WaitForSingleObject(e.config.SignalEvent, uint32(timeout/time.Millisecond))
	if err != nil {
		return false, fmt.Errorf("windows.WaitForSingleObject failed: %w", err)
	}
	return status == syscall.WAIT_OBJECT_0, nil
}

// RenderedEvents returns the rendered events as a slice of UTF8 formatted XML strings. `done` will
// be true if no more events.
func (e *WindowsEvent) RenderedEvents(max int) (events []string, done bool, err error) {
	events, err = winlog.GetRenderedEvents(e.config, e.subscription, max, 1033)
	// Windows sometimes reports ERROR_INVALID_OPERATION when there is
	// nothing to read. Look, others have encountered the same:
	// https://github.com/elastic/beats/issues/3076#issuecomment-264449775
	if err == windows.ERROR_INVALID_OPERATION || err == windows.ERROR_NO_MORE_ITEMS {
		return nil, true, nil
	} else if err != nil {
		return nil, false, err
	}
	return events, false, nil
}

// Bookmark returns the bookmark in XML format.
func (e *WindowsEvent) Bookmark() (string, error) {
	return winlog.RenderFragment(e.config.Bookmark, wevtapi.EvtRenderBookmark)
}

// ResetEvent resets the event signal after all events are read.
func (e *WindowsEvent) ResetEvent() error {
	return windows.ResetEvent(e.config.SignalEvent)
}

// Close closes the subscription of Windows Event Log and releases the resource.
func (e *WindowsEvent) Close() error {
	if err := winlog.Close(e.subscription); err != nil {
		return err
	}
	e.subscription = 0
	e.config = nil
	return nil
}
<commit_msg>Internal Change<commit_after>// +build windows

package simple

import (
	"fmt"
	"strings"
	"syscall"
	"time"

	log "github.com/golang/glog"
	"golang.org/x/sys/windows"
	"github.com/google/winops/winlog/wevtapi"
	"github.com/google/winops/winlog"
)

// WindowsEvent implements Event interface to subscribe events from Windows Event Log.
type WindowsEvent struct {
	config       *winlog.SubscribeConfig
	subscription windows.Handle
}

// NewWindowsEvent creates a WindowsEvent object.
func NewWindowsEvent() Event {
	return &WindowsEvent{}
}

// Subscribe creates the bookmark from XML formatted string and initializes a subscription for
// Windows Event Log.
func (e *WindowsEvent) Subscribe(bookmark string, query map[string]string) error {
	if e.config != nil || e.subscription != 0 {
		return fmt.Errorf("double subscribed Windows Event for: %+v", e)
	}

	var err error
	e.config, err = winlog.DefaultSubscribeConfig()
	if err != nil {
		return fmt.Errorf("winlog.DefaultSubscribeConfig failed: %w", err)
	}
	if bookmark == "" {
		e.config.Bookmark, err = wevtapi.EvtCreateBookmark(nil)
		if err != nil {
			return fmt.Errorf("wevtapi.EvtCreateBookmark failed: %w", err)
		}
	} else {
		e.config.Bookmark, err = wevtapi.EvtCreateBookmark(syscall.StringToUTF16Ptr(bookmark))
		if err != nil {
			log.Warningf("Create a new bookmark because the existing bookmark might be corrupted: %s", bookmark)
			e.config.Bookmark, err = wevtapi.EvtCreateBookmark(nil)
			if err != nil {
				return fmt.Errorf("wevtapi.EvtCreateBookmark failed: %w", err)
			}
		}
	}

	if len(query) != 0 {
		channels, err := winlog.AvailableChannels()
		if err != nil {
			return fmt.Errorf("finding available channels: %w", err)
		}
		channelSet := make(map[string]bool)
		for _, c := range channels {
			channelSet[strings.ToLower(c)] = true
		}
		// Filter out channels that are not available
		q := make(map[string]string)
		for k, v := range query {
			if channelSet[strings.ToLower(k)] {
				q[k] = v
			} else {
				log.Warningf("Ignoring non-existent Windows Event Log channel %q", k)
			}
		}
		xmlQuery, err := winlog.BuildStructuredXMLQuery(q)
		if err != nil {
			return fmt.Errorf("Build structured XML query error: %w", err)
		}
		log.V(1).Infof("Built the structured XML Query: %s", xmlQuery)
		e.config.Query, err = syscall.UTF16PtrFromString(string(xmlQuery))
		if err != nil {
			return fmt.Errorf("syscall.UTF16PtrFromString failed: %w", err)
		}
	}

	e.config.Flags = wevtapi.EvtSubscribeStartAfterBookmark
	e.subscription, err = winlog.Subscribe(e.config)
	return err
}

// WaitForSingleObject waits for new events to arrive. Returns true if the event
// was signalled before the timeout expired. Timeout must be between 0 and 2^32us.
func (e *WindowsEvent) WaitForSingleObject(timeout time.Duration) (bool, error) {
	status, err := windows.WaitForSingleObject(e.config.SignalEvent, uint32(timeout/time.Millisecond))
	if err != nil {
		return false, fmt.Errorf("windows.WaitForSingleObject failed: %w", err)
	}
	return status == syscall.WAIT_OBJECT_0, nil
}

// RenderedEvents returns the rendered events as a slice of UTF8 formatted XML strings. `done` will
// be true if no more events.
func (e *WindowsEvent) RenderedEvents(max int) (events []string, done bool, err error) {
	events, err = winlog.GetRenderedEvents(e.config, e.subscription, max, 1033)
	// Windows sometimes reports ERROR_INVALID_OPERATION when there is
	// nothing to read. Look, others have encountered the same:
	// https://github.com/elastic/beats/issues/3076#issuecomment-264449775
	if err == windows.ERROR_INVALID_OPERATION || err == windows.ERROR_NO_MORE_ITEMS {
		return nil, true, nil
	} else if err != nil {
		return nil, false, err
	}
	return events, false, nil
}

// Bookmark returns the bookmark in XML format.
func (e *WindowsEvent) Bookmark() (string, error) {
	return winlog.RenderFragment(e.config.Bookmark, wevtapi.EvtRenderBookmark)
}

// ResetEvent resets the event signal after all events are read.
func (e *WindowsEvent) ResetEvent() error {
	return windows.ResetEvent(e.config.SignalEvent)
}

// Close closes the subscription of Windows Event Log and releases the resource.
func (e *WindowsEvent) Close() error {
	if err := winlog.Close(e.subscription); err != nil {
		return err
	}
	e.subscription = 0
	e.config = nil
	return nil
}
<|endoftext|>


<commit_before>package boardgame

import (
	"encoding/json"
	"io/ioutil"
	"testing"
)

func TestState(t *testing.T) {

	game := testGame()

	state := game.State

	if state == nil {
		t.Error("State could not be created")
	}

	json := state.JSON()
	golden := goldenJSON("basic_state.json", t)

	compareJSONObjects(json, golden, "Basic state", t)

	stateCopy := state.Copy()

	compareJSONObjects(stateCopy.JSON(), state.JSON(), "Copy was not same", t)

	stateCopy.Schema = 1

	if state.Schema == 1 {
		t.Error("Modifying a copy changed the original")
	}

	//TODO: test that GAmeState and UserStates are also copies
}

func compareJSONObjects(in JSONObject, golden JSONObject, message string, t *testing.T) {
	serializedIn := string(Serialize(in))
	serializedGolden := string(Serialize(golden))
	if serializedIn != serializedGolden {
		t.Error("Got wrong json.", message, "Got", serializedIn, "wanted", serializedGolden)
	}
}

func goldenJSON(fileName string, t *testing.T) JSONObject {
	contents, err := ioutil.ReadFile("./test/" + fileName)
	if err != nil {
		t.Fatal("Couldn't load golden JSON at " + fileName)
	}

	result := make(JSONMap)

	if err := json.Unmarshal(contents, &result); err != nil {
		t.Fatal("Couldn't parse golden json at " + fileName + err.Error())
	}

	return result

}
<commit_msg>Switched out we check if json objects are the same in tests. should be more resilient.<commit_after>package boardgame

import (
	"encoding/json"
	"io/ioutil"
	"reflect"
	"testing"
)

func TestState(t *testing.T) {

	game := testGame()

	state := game.State

	if state == nil {
		t.Error("State could not be created")
	}

	json := state.JSON()
	golden := goldenJSON("basic_state.json", t)

	compareJSONObjects(json, golden, "Basic state", t)

	stateCopy := state.Copy()

	compareJSONObjects(stateCopy.JSON(), state.JSON(), "Copy was not same", t)

	stateCopy.Schema = 1

	if state.Schema == 1 {
		t.Error("Modifying a copy changed the original")
	}

	//TODO: test that GAmeState and UserStates are also copies
}

func compareJSONObjects(in JSONObject, golden JSONObject, message string, t *testing.T) {
	serializedIn := Serialize(in)
	serializedGolden := Serialize(golden)

	var deserializedIn interface{}
	var deserializedGolden interface{}

	json.Unmarshal(serializedIn, &deserializedIn)
	json.Unmarshal(serializedGolden, &deserializedGolden)

	if !reflect.DeepEqual(deserializedIn, deserializedGolden) {
		t.Error("Got wrong json.", message, "Got", string(serializedIn), "wanted", string(serializedGolden))
	}
}

func goldenJSON(fileName string, t *testing.T) JSONObject {
	contents, err := ioutil.ReadFile("./test/" + fileName)
	if err != nil {
		t.Fatal("Couldn't load golden JSON at " + fileName)
	}

	result := make(JSONMap)

	if err := json.Unmarshal(contents, &result); err != nil {
		t.Fatal("Couldn't parse golden json at " + fileName + err.Error())
	}

	return result

}
<|endoftext|>


<commit_before>package gorpc

import (
	"crypto/tls"
	"io"
	"net"
	"time"
)

var (
	dialer = &net.Dialer{
		Timeout:   10 * time.Second,
		KeepAlive: 30 * time.Second,
	}
)

// DialFunc is a function intended for setting to Client.Dial.
//
// It is expected that the returned conn immediately
// sends all the data passed via Write() to the server.
// Otherwise gorpc may hang.
// The conn implementation must call Flush() on underlying buffered
// streams before returning from Write().
type DialFunc func(addr string) (conn io.ReadWriteCloser, err error)

// Listener is an interface for custom listeners intended for the Server.
type Listener interface {
	// Init is called on server start.
	//
	// addr contains the address set at Server.Addr.
	Init(addr string) error

	// Accept must return incoming connections from clients.
	// clientAddr must contain client's address in user-readable view.
	//
	// It is expected that the returned conn immediately
	// sends all the data passed via Write() to the client.
	// Otherwise gorpc may hang.
	// The conn implementation must call Flush() on underlying buffered
	// streams before returning from Write().
	Accept() (conn io.ReadWriteCloser, clientAddr string, err error)

	// Close closes the listener.
	// All pending calls to Accept() must immediately return errors after
	// Close is called.
	// All subsequent calls to Accept() must immediately return error.
	Close() error
}

func defaultDial(addr string) (conn io.ReadWriteCloser, err error) {
	return dialer.Dial("tcp", addr)
}

type defaultListener struct {
	L net.Listener
}

func (ln *defaultListener) Init(addr string) (err error) {
	ln.L, err = net.Listen("tcp", addr)
	return
}

func (ln *defaultListener) Accept() (conn io.ReadWriteCloser, clientAddr string, err error) {
	c, err := ln.L.Accept()
	if err != nil {
		return nil, "", err
	}
	if err = setupKeepalive(c); err != nil {
		c.Close()
		return nil, "", err
	}
	return c, c.RemoteAddr().String(), nil
}

func (ln *defaultListener) Close() error {
	return ln.L.Close()
}

func setupKeepalive(conn net.Conn) error {
	tcpConn := conn.(*net.TCPConn)
	if err := tcpConn.SetKeepAlive(true); err != nil {
		return err
	}
	if err := tcpConn.SetKeepAlivePeriod(30 * time.Second); err != nil {
		return err
	}
	return nil
}

type netListener struct {
	F func(addr string) (net.Listener, error)
	L net.Listener
}

func (ln *netListener) Init(addr string) (err error) {
	ln.L, err = ln.F(addr)
	return
}

func (ln *netListener) Accept() (conn io.ReadWriteCloser, clientAddr string, err error) {
	c, err := ln.L.Accept()
	if err != nil {
		return nil, "", err
	}
	return c, c.RemoteAddr().String(), nil
}

func (ln *netListener) Close() error {
	return ln.L.Close()
}

func unixDial(addr string) (conn io.ReadWriteCloser, err error) {
	c, err := net.Dial("unix", addr)
	if err != nil {
		return nil, err
	}
	return c, err
}

// NewUnixClient creates a client connecting over TCP to the server
// listening to the given addr.
//
// The returned client must be started after optional settings' adjustment.
//
// The corresponding server must be created with NewTCPServer().
func NewTCPClient(addr string) *Client {
	return &Client{
		Addr: addr,
		Dial: defaultDial,
	}
}

// NewTCPServer creates a server listening for TCP connections
// on the given addr and processing incoming requests
// with the given HandlerFunc.
//
// The returned server must be started after optional settings' adjustment.
//
// The corresponding client must be created with NewTCPClient().
func NewTCPServer(addr string, handler HandlerFunc) *Server {
	return &Server{
		Addr:     addr,
		Handler:  handler,
		Listener: &defaultListener{},
	}
}

// NewUnixClient creates a client connecting over unix socket
// to the server listening to the given addr.
//
// The returned client must be started after optional settings' adjustment.
//
// The corresponding server must be created with NewUnixServer().
func NewUnixClient(addr string) *Client {
	return &Client{
		Addr: addr,
		Dial: unixDial,

		// There is little sense in compressing rpc data passed
		// over local unix sockets.
		DisableCompression: true,

		// Sacrifice the number of Write() calls by smallest possible
		// latency, since it has higher priority in local IPC.
		FlushDelay: -1,
	}
}

// NewUnixServer creates a server listening for unix connections
// on the given addr and processing incoming requests
// with the given HandlerFunc.
//
// The returned server must be started after optional settings' adjustment.
//
// The corresponding client must be created with NewUnixClient().
func NewUnixServer(addr string, handler HandlerFunc) *Server {
	return &Server{
		Addr:    addr,
		Handler: handler,
		Listener: &netListener{
			F: func(addr string) (net.Listener, error) {
				return net.Listen("unix", addr)
			},
		},
	}
}

// NewTLSClient creates a client connecting over TLS to the server
// listening to the given addr using the given TLS config.
//
// The returned client must be started after optional settings' adjustment.
//
// The corresponding server must be created with NewTLSServer().
func NewTLSClient(addr string, cfg *tls.Config) *Client {
	return &Client{
		Addr: addr,
		Dial: func(addr string) (conn io.ReadWriteCloser, err error) {
			c, err := tls.DialWithDialer(dialer, "tcp", addr, cfg)
			if err != nil {
				return nil, err
			}
			return c, err
		},
	}
}

// NewTLSServer creates a server listening for TLS connections
// on the given addr and processing incoming requests
// with the given HandlerFunc.
// cfg must contain TLS settings for the server.
//
// The returned server must be started after optional settings' adjustment.
//
// The corresponding client must be created with NewTLSClient().
func NewTLSServer(addr string, handler HandlerFunc, cfg *tls.Config) *Server {
	return &Server{
		Addr:    addr,
		Handler: handler,
		Listener: &netListener{
			F: func(addr string) (net.Listener, error) {
				return tls.Listen("tcp", addr, cfg)
			},
		},
	}
}
<commit_msg>Fixed a typo<commit_after>package gorpc

import (
	"crypto/tls"
	"io"
	"net"
	"time"
)

var (
	dialer = &net.Dialer{
		Timeout:   10 * time.Second,
		KeepAlive: 30 * time.Second,
	}
)

// DialFunc is a function intended for setting to Client.Dial.
//
// It is expected that the returned conn immediately
// sends all the data passed via Write() to the server.
// Otherwise gorpc may hang.
// The conn implementation must call Flush() on underlying buffered
// streams before returning from Write().
type DialFunc func(addr string) (conn io.ReadWriteCloser, err error)

// Listener is an interface for custom listeners intended for the Server.
type Listener interface {
	// Init is called on server start.
	//
	// addr contains the address set at Server.Addr.
	Init(addr string) error

	// Accept must return incoming connections from clients.
	// clientAddr must contain client's address in user-readable view.
	//
	// It is expected that the returned conn immediately
	// sends all the data passed via Write() to the client.
	// Otherwise gorpc may hang.
	// The conn implementation must call Flush() on underlying buffered
	// streams before returning from Write().
	Accept() (conn io.ReadWriteCloser, clientAddr string, err error)

	// Close closes the listener.
	// All pending calls to Accept() must immediately return errors after
	// Close is called.
	// All subsequent calls to Accept() must immediately return error.
	Close() error
}

func defaultDial(addr string) (conn io.ReadWriteCloser, err error) {
	return dialer.Dial("tcp", addr)
}

type defaultListener struct {
	L net.Listener
}

func (ln *defaultListener) Init(addr string) (err error) {
	ln.L, err = net.Listen("tcp", addr)
	return
}

func (ln *defaultListener) Accept() (conn io.ReadWriteCloser, clientAddr string, err error) {
	c, err := ln.L.Accept()
	if err != nil {
		return nil, "", err
	}
	if err = setupKeepalive(c); err != nil {
		c.Close()
		return nil, "", err
	}
	return c, c.RemoteAddr().String(), nil
}

func (ln *defaultListener) Close() error {
	return ln.L.Close()
}

func setupKeepalive(conn net.Conn) error {
	tcpConn := conn.(*net.TCPConn)
	if err := tcpConn.SetKeepAlive(true); err != nil {
		return err
	}
	if err := tcpConn.SetKeepAlivePeriod(30 * time.Second); err != nil {
		return err
	}
	return nil
}

type netListener struct {
	F func(addr string) (net.Listener, error)
	L net.Listener
}

func (ln *netListener) Init(addr string) (err error) {
	ln.L, err = ln.F(addr)
	return
}

func (ln *netListener) Accept() (conn io.ReadWriteCloser, clientAddr string, err error) {
	c, err := ln.L.Accept()
	if err != nil {
		return nil, "", err
	}
	return c, c.RemoteAddr().String(), nil
}

func (ln *netListener) Close() error {
	return ln.L.Close()
}

func unixDial(addr string) (conn io.ReadWriteCloser, err error) {
	c, err := net.Dial("unix", addr)
	if err != nil {
		return nil, err
	}
	return c, err
}

// NewTCPClient creates a client connecting over TCP to the server
// listening to the given addr.
//
// The returned client must be started after optional settings' adjustment.
//
// The corresponding server must be created with NewTCPServer().
func NewTCPClient(addr string) *Client {
	return &Client{
		Addr: addr,
		Dial: defaultDial,
	}
}

// NewTCPServer creates a server listening for TCP connections
// on the given addr and processing incoming requests
// with the given HandlerFunc.
//
// The returned server must be started after optional settings' adjustment.
//
// The corresponding client must be created with NewTCPClient().
func NewTCPServer(addr string, handler HandlerFunc) *Server {
	return &Server{
		Addr:     addr,
		Handler:  handler,
		Listener: &defaultListener{},
	}
}

// NewUnixClient creates a client connecting over unix socket
// to the server listening to the given addr.
//
// The returned client must be started after optional settings' adjustment.
//
// The corresponding server must be created with NewUnixServer().
func NewUnixClient(addr string) *Client {
	return &Client{
		Addr: addr,
		Dial: unixDial,

		// There is little sense in compressing rpc data passed
		// over local unix sockets.
		DisableCompression: true,

		// Sacrifice the number of Write() calls by smallest possible
		// latency, since it has higher priority in local IPC.
		FlushDelay: -1,
	}
}

// NewUnixServer creates a server listening for unix connections
// on the given addr and processing incoming requests
// with the given HandlerFunc.
//
// The returned server must be started after optional settings' adjustment.
//
// The corresponding client must be created with NewUnixClient().
func NewUnixServer(addr string, handler HandlerFunc) *Server {
	return &Server{
		Addr:    addr,
		Handler: handler,
		Listener: &netListener{
			F: func(addr string) (net.Listener, error) {
				return net.Listen("unix", addr)
			},
		},
	}
}

// NewTLSClient creates a client connecting over TLS to the server
// listening to the given addr using the given TLS config.
//
// The returned client must be started after optional settings' adjustment.
//
// The corresponding server must be created with NewTLSServer().
func NewTLSClient(addr string, cfg *tls.Config) *Client {
	return &Client{
		Addr: addr,
		Dial: func(addr string) (conn io.ReadWriteCloser, err error) {
			c, err := tls.DialWithDialer(dialer, "tcp", addr, cfg)
			if err != nil {
				return nil, err
			}
			return c, err
		},
	}
}

// NewTLSServer creates a server listening for TLS connections
// on the given addr and processing incoming requests
// with the given HandlerFunc.
// cfg must contain TLS settings for the server.
//
// The returned server must be started after optional settings' adjustment.
//
// The corresponding client must be created with NewTLSClient().
func NewTLSServer(addr string, handler HandlerFunc, cfg *tls.Config) *Server {
	return &Server{
		Addr:    addr,
		Handler: handler,
		Listener: &netListener{
			F: func(addr string) (net.Listener, error) {
				return tls.Listen("tcp", addr, cfg)
			},
		},
	}
}
<|endoftext|>


<commit_before>package httpmock

import (
	"errors"
	"fmt"
	"net/http"
	"strings"
)

// Responders are callbacks that receive and http request and return a mocked response.
type Responder func(*http.Request) (*http.Response, error)

// NoResponderFound is returned when no responders are found for a given HTTP method and URL.
var NoResponderFound = errors.New("no responder found")

// ConnectionFailure is a responder that returns a connection failure.  This is the default
// responder, and is called when no other matching responder is found.
func ConnectionFailure(*http.Request) (*http.Response, error) {
	return nil, NoResponderFound
}

// NewMockTransport creates a new *MockTransport with no responders.
func NewMockTransport() *MockTransport {
	return &MockTransport{make(map[string]Responder), nil, make(map[string]int), 0}
}

// MockTransport implements http.RoundTripper, which fulfills single http requests issued by
// an http.Client.  This implementation doesn't actually make the call, instead deferring to
// the registered list of responders.
type MockTransport struct {
	responders     map[string]Responder
	noResponder    Responder
	callCountInfo  map[string]int
	totalCallCount int
}

// RoundTrip receives HTTP requests and routes them to the appropriate responder.  It is required to
// implement the http.RoundTripper interface.  You will not interact with this directly, instead
// the *http.Client you are using will call it for you.
func (m *MockTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	url := req.URL.String()

	// try and get a responder that matches the method and URL
	key := req.Method + " " + url
	responder := m.responderForKey(key)

	// if we weren't able to find a responder and the URL contains a querystring
	// then we strip off the querystring and try again.
	if responder == nil && strings.Contains(url, "?") {
		key = req.Method + " " + strings.Split(url, "?")[0]
		responder = m.responderForKey(key)
	}

	// if we found a responder, call it
	if responder != nil {
		m.callCountInfo[key]++
		m.totalCallCount++
		return runCancelable(responder, req)
	}

	// we didn't find a responder, so fire the 'no responder' responder
	if m.noResponder == nil {
		return ConnectionFailure(req)
	}
	m.callCountInfo["NO_RESPONDER"]++
	m.totalCallCount++
	return runCancelable(m.noResponder, req)
}

func runCancelable(responder Responder, req *http.Request) (*http.Response, error) {
	if req.Cancel == nil {
		return responder(req)
	}

	// Set up a goroutine that translates a close(req.Cancel) into a
	// "request canceled" error, and another one that runs the
	// responder. Then race them: first to the result channel wins.

	type result struct {
		response *http.Response
		err      error
	}
	resultch := make(chan result, 1)
	done := make(chan struct{}, 1)

	go func() {
		select {
		case <-req.Cancel:
			resultch <- result{
				response: nil,
				err:      errors.New("request canceled"),
			}
		case <-done:
		}
	}()

	go func() {
		defer func() {
			if err := recover(); err != nil {
				resultch <- result{
					response: nil,
					err:      fmt.Errorf("panic in responder: got %q", err),
				}
			}
		}()

		response, err := responder(req)
		resultch <- result{
			response: response,
			err:      err,
		}
	}()

	r := <-resultch

	// if a close(req.Cancel) is never coming,
	// we'll need to unblock the first goroutine.
	done <- struct{}{}

	return r.response, r.err
}

// do nothing with timeout
func (m *MockTransport) CancelRequest(req *http.Request) {}

// responderForKey returns a responder for a given key
func (m *MockTransport) responderForKey(key string) Responder {
	for k, r := range m.responders {
		if k != key {
			continue
		}
		return r
	}
	return nil
}

// RegisterResponder adds a new responder, associated with a given HTTP method and URL.  When a
// request comes in that matches, the responder will be called and the response returned to the client.
func (m *MockTransport) RegisterResponder(method, url string, responder Responder) {
	key := method + " " + url

	m.responders[key] = responder
	m.callCountInfo[key] = 0
}

// RegisterNoResponder is used to register a responder that will be called if no other responder is
// found.  The default is ConnectionFailure.
func (m *MockTransport) RegisterNoResponder(responder Responder) {
	m.noResponder = responder
}

// Reset removes all registered responders (including the no responder) from the MockTransport
func (m *MockTransport) Reset() {
	m.responders = make(map[string]Responder)
	m.noResponder = nil
	m.callCountInfo = make(map[string]int)
	m.totalCallCount = 0
}

// GetCallCountInfo returns callCountInfo
func (m *MockTransport) GetCallCountInfo() map[string]int {
	return m.callCountInfo
}

// GetTotalCallCount returns the totalCallCount
func (m *MockTransport) GetTotalCallCount() int {
	return m.totalCallCount
}

// DefaultTransport is the default mock transport used by Activate, Deactivate, Reset,
// DeactivateAndReset, RegisterResponder, and RegisterNoResponder.
var DefaultTransport = NewMockTransport()

// InitialTransport is a cache of the original transport used so we can put it back
// when Deactivate is called.
var InitialTransport = http.DefaultTransport

// Used to handle custom http clients (i.e clients other than http.DefaultClient)
var oldTransport http.RoundTripper
var oldClient *http.Client

// Activate starts the mock environment.  This should be called before your tests run.  Under the
// hood this replaces the Transport on the http.DefaultClient with DefaultTransport.
//
// To enable mocks for a test, simply activate at the beginning of a test:
// 		func TestFetchArticles(t *testing.T) {
// 			httpmock.Activate()
// 			// all http requests will now be intercepted
// 		}
//
// If you want all of your tests in a package to be mocked, just call Activate from init():
// 		func init() {
// 			httpmock.Activate()
// 		}
func Activate() {
	if Disabled() {
		return
	}

	// make sure that if Activate is called multiple times it doesn't overwrite the InitialTransport
	// with a mock transport.
	if http.DefaultTransport != DefaultTransport {
		InitialTransport = http.DefaultTransport
	}

	http.DefaultTransport = DefaultTransport
}

// ActivateNonDefault starts the mock environment with a non-default http.Client.
// This emulates the Activate function, but allows for custom clients that do not use
// http.DefaultTransport
//
// To enable mocks for a test using a custom client, activate at the beginning of a test:
// 		client := &http.Client{Transport: &http.Transport{TLSHandshakeTimeout: 60 * time.Second}}
// 		httpmock.ActivateNonDefault(client)
func ActivateNonDefault(client *http.Client) {
	if Disabled() {
		return
	}

	// save the custom client & it's RoundTripper
	oldTransport = client.Transport
	oldClient = client
	client.Transport = DefaultTransport
}

// GetCallCountInfo gets the info on all the calls httpmock has taken since it was activated or
// reset. The info is returned as a map of the calling keys with the number of calls made to them
// as their value. The key is the method, a space, and the url all concatenated together.
func GetCallCountInfo() map[string]int {
	return DefaultTransport.GetCallCountInfo()
}

// GetTotalCallCount gets the total number of calls httpmock has taken since it was activated or
// reset.
func GetTotalCallCount() int {
	return DefaultTransport.GetTotalCallCount()
}

// Deactivate shuts down the mock environment.  Any HTTP calls made after this will use a live
// transport.
//
// Usually you'll call it in a defer right after activating the mock environment:
// 		func TestFetchArticles(t *testing.T) {
// 			httpmock.Activate()
// 			defer httpmock.Deactivate()
//
// 			// when this test ends, the mock environment will close
// 		}
func Deactivate() {
	if Disabled() {
		return
	}
	http.DefaultTransport = InitialTransport

	// reset the custom client to use it's original RoundTripper
	if oldClient != nil {
		oldClient.Transport = oldTransport
	}
}

// Reset will remove any registered mocks and return the mock environment to it's initial state.
func Reset() {
	DefaultTransport.Reset()
}

// DeactivateAndReset is just a convenience method for calling Deactivate() and then Reset()
// Happy deferring!
func DeactivateAndReset() {
	Deactivate()
	Reset()
}

// RegisterResponder adds a mock that will catch requests to the given HTTP method and URL, then
// route them to the Responder which will generate a response to be returned to the client.
//
// Example:
// 		func TestFetchArticles(t *testing.T) {
// 			httpmock.Activate()
// 			httpmock.DeactivateAndReset()
//
// 			httpmock.RegisterResponder("GET", "http://example.com/",
// 				httpmock.NewStringResponder("hello world", 200))
//
//			// requests to http://example.com/ will now return 'hello world'
// 		}
func RegisterResponder(method, url string, responder Responder) {
	DefaultTransport.RegisterResponder(method, url, responder)
}

// RegisterNoResponder adds a mock that will be called whenever a request for an unregistered URL
// is received.  The default behavior is to return a connection error.
//
// In some cases you may not want all URLs to be mocked, in which case you can do this:
// 		func TestFetchArticles(t *testing.T) {
// 			httpmock.Activate()
// 			httpmock.DeactivateAndReset()
//			httpmock.RegisterNoResponder(httpmock.InitialTransport.RoundTrip)
//
// 			// any requests that don't have a registered URL will be fetched normally
// 		}
func RegisterNoResponder(responder Responder) {
	DefaultTransport.RegisterNoResponder(responder)
}
<commit_msg>fix data races in concurrent use of MockTransport (#29)<commit_after>package httpmock

import (
	"errors"
	"fmt"
	"net/http"
	"strings"
	"sync"
)

// Responders are callbacks that receive and http request and return a mocked response.
type Responder func(*http.Request) (*http.Response, error)

// NoResponderFound is returned when no responders are found for a given HTTP method and URL.
var NoResponderFound = errors.New("no responder found")

// ConnectionFailure is a responder that returns a connection failure.  This is the default
// responder, and is called when no other matching responder is found.
func ConnectionFailure(*http.Request) (*http.Response, error) {
	return nil, NoResponderFound
}

// NewMockTransport creates a new *MockTransport with no responders.
func NewMockTransport() *MockTransport {
	return &MockTransport{
		responders:    make(map[string]Responder),
		callCountInfo: make(map[string]int),
	}
}

// MockTransport implements http.RoundTripper, which fulfills single http requests issued by
// an http.Client.  This implementation doesn't actually make the call, instead deferring to
// the registered list of responders.
type MockTransport struct {
	mu             sync.RWMutex
	responders     map[string]Responder
	noResponder    Responder
	callCountInfo  map[string]int
	totalCallCount int
}

// RoundTrip receives HTTP requests and routes them to the appropriate responder.  It is required to
// implement the http.RoundTripper interface.  You will not interact with this directly, instead
// the *http.Client you are using will call it for you.
func (m *MockTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	url := req.URL.String()

	// try and get a responder that matches the method and URL
	key := req.Method + " " + url
	responder := m.responderForKey(key)

	// if we weren't able to find a responder and the URL contains a querystring
	// then we strip off the querystring and try again.
	if responder == nil && strings.Contains(url, "?") {
		key = req.Method + " " + strings.Split(url, "?")[0]
		responder = m.responderForKey(key)
	}
	m.mu.Lock()
	defer m.mu.Unlock()
	// if we found a responder, call it
	if responder != nil {
		m.callCountInfo[key]++
		m.totalCallCount++
		return runCancelable(responder, req)
	}

	// we didn't find a responder, so fire the 'no responder' responder
	if m.noResponder == nil {
		return ConnectionFailure(req)
	}
	m.callCountInfo["NO_RESPONDER"]++
	m.totalCallCount++
	return runCancelable(m.noResponder, req)
}

func runCancelable(responder Responder, req *http.Request) (*http.Response, error) {
	if req.Cancel == nil {
		return responder(req)
	}

	// Set up a goroutine that translates a close(req.Cancel) into a
	// "request canceled" error, and another one that runs the
	// responder. Then race them: first to the result channel wins.

	type result struct {
		response *http.Response
		err      error
	}
	resultch := make(chan result, 1)
	done := make(chan struct{}, 1)

	go func() {
		select {
		case <-req.Cancel:
			resultch <- result{
				response: nil,
				err:      errors.New("request canceled"),
			}
		case <-done:
		}
	}()

	go func() {
		defer func() {
			if err := recover(); err != nil {
				resultch <- result{
					response: nil,
					err:      fmt.Errorf("panic in responder: got %q", err),
				}
			}
		}()

		response, err := responder(req)
		resultch <- result{
			response: response,
			err:      err,
		}
	}()

	r := <-resultch

	// if a close(req.Cancel) is never coming,
	// we'll need to unblock the first goroutine.
	done <- struct{}{}

	return r.response, r.err
}

// do nothing with timeout
func (m *MockTransport) CancelRequest(req *http.Request) {}

// responderForKey returns a responder for a given key
func (m *MockTransport) responderForKey(key string) Responder {
	m.mu.RLock()
	defer m.mu.RUnlock()
	for k, r := range m.responders {
		if k != key {
			continue
		}
		return r
	}
	return nil
}

// RegisterResponder adds a new responder, associated with a given HTTP method and URL.  When a
// request comes in that matches, the responder will be called and the response returned to the client.
func (m *MockTransport) RegisterResponder(method, url string, responder Responder) {
	key := method + " " + url

	m.mu.Lock()
	m.responders[key] = responder
	m.callCountInfo[key] = 0
	m.mu.Unlock()
}

// RegisterNoResponder is used to register a responder that will be called if no other responder is
// found.  The default is ConnectionFailure.
func (m *MockTransport) RegisterNoResponder(responder Responder) {
	m.mu.Lock()
	m.noResponder = responder
	m.mu.Unlock()
}

// Reset removes all registered responders (including the no responder) from the MockTransport
func (m *MockTransport) Reset() {
	m.mu.Lock()
	m.responders = make(map[string]Responder)
	m.noResponder = nil
	m.callCountInfo = make(map[string]int)
	m.totalCallCount = 0
	m.mu.Unlock()
}

// GetCallCountInfo returns callCountInfo
func (m *MockTransport) GetCallCountInfo() map[string]int {
	res := map[string]int{}
	m.mu.RLock()
	for k, v := range m.callCountInfo {
		res[k] = v
	}
	m.mu.RUnlock()
	return res
}

// GetTotalCallCount returns the totalCallCount
func (m *MockTransport) GetTotalCallCount() int {
	m.mu.RLock()
	count := m.totalCallCount
	m.mu.RUnlock()
	return count
}

// DefaultTransport is the default mock transport used by Activate, Deactivate, Reset,
// DeactivateAndReset, RegisterResponder, and RegisterNoResponder.
var DefaultTransport = NewMockTransport()

// InitialTransport is a cache of the original transport used so we can put it back
// when Deactivate is called.
var InitialTransport = http.DefaultTransport

// Used to handle custom http clients (i.e clients other than http.DefaultClient)
var oldTransport http.RoundTripper
var oldClient *http.Client

// Activate starts the mock environment.  This should be called before your tests run.  Under the
// hood this replaces the Transport on the http.DefaultClient with DefaultTransport.
//
// To enable mocks for a test, simply activate at the beginning of a test:
// 		func TestFetchArticles(t *testing.T) {
// 			httpmock.Activate()
// 			// all http requests will now be intercepted
// 		}
//
// If you want all of your tests in a package to be mocked, just call Activate from init():
// 		func init() {
// 			httpmock.Activate()
// 		}
func Activate() {
	if Disabled() {
		return
	}

	// make sure that if Activate is called multiple times it doesn't overwrite the InitialTransport
	// with a mock transport.
	if http.DefaultTransport != DefaultTransport {
		InitialTransport = http.DefaultTransport
	}

	http.DefaultTransport = DefaultTransport
}

// ActivateNonDefault starts the mock environment with a non-default http.Client.
// This emulates the Activate function, but allows for custom clients that do not use
// http.DefaultTransport
//
// To enable mocks for a test using a custom client, activate at the beginning of a test:
// 		client := &http.Client{Transport: &http.Transport{TLSHandshakeTimeout: 60 * time.Second}}
// 		httpmock.ActivateNonDefault(client)
func ActivateNonDefault(client *http.Client) {
	if Disabled() {
		return
	}

	// save the custom client & it's RoundTripper
	oldTransport = client.Transport
	oldClient = client
	client.Transport = DefaultTransport
}

// GetCallCountInfo gets the info on all the calls httpmock has taken since it was activated or
// reset. The info is returned as a map of the calling keys with the number of calls made to them
// as their value. The key is the method, a space, and the url all concatenated together.
func GetCallCountInfo() map[string]int {
	return DefaultTransport.GetCallCountInfo()
}

// GetTotalCallCount gets the total number of calls httpmock has taken since it was activated or
// reset.
func GetTotalCallCount() int {
	return DefaultTransport.GetTotalCallCount()
}

// Deactivate shuts down the mock environment.  Any HTTP calls made after this will use a live
// transport.
//
// Usually you'll call it in a defer right after activating the mock environment:
// 		func TestFetchArticles(t *testing.T) {
// 			httpmock.Activate()
// 			defer httpmock.Deactivate()
//
// 			// when this test ends, the mock environment will close
// 		}
func Deactivate() {
	if Disabled() {
		return
	}
	http.DefaultTransport = InitialTransport

	// reset the custom client to use it's original RoundTripper
	if oldClient != nil {
		oldClient.Transport = oldTransport
	}
}

// Reset will remove any registered mocks and return the mock environment to it's initial state.
func Reset() {
	DefaultTransport.Reset()
}

// DeactivateAndReset is just a convenience method for calling Deactivate() and then Reset()
// Happy deferring!
func DeactivateAndReset() {
	Deactivate()
	Reset()
}

// RegisterResponder adds a mock that will catch requests to the given HTTP method and URL, then
// route them to the Responder which will generate a response to be returned to the client.
//
// Example:
// 		func TestFetchArticles(t *testing.T) {
// 			httpmock.Activate()
// 			httpmock.DeactivateAndReset()
//
// 			httpmock.RegisterResponder("GET", "http://example.com/",
// 				httpmock.NewStringResponder("hello world", 200))
//
//			// requests to http://example.com/ will now return 'hello world'
// 		}
func RegisterResponder(method, url string, responder Responder) {
	DefaultTransport.RegisterResponder(method, url, responder)
}

// RegisterNoResponder adds a mock that will be called whenever a request for an unregistered URL
// is received.  The default behavior is to return a connection error.
//
// In some cases you may not want all URLs to be mocked, in which case you can do this:
// 		func TestFetchArticles(t *testing.T) {
// 			httpmock.Activate()
// 			httpmock.DeactivateAndReset()
//			httpmock.RegisterNoResponder(httpmock.InitialTransport.RoundTrip)
//
// 			// any requests that don't have a registered URL will be fetched normally
// 		}
func RegisterNoResponder(responder Responder) {
	DefaultTransport.RegisterNoResponder(responder)
}
<|endoftext|>


<commit_before>package wsmaterials

import (
	"fmt"
	"github.com/emicklei/go-restful"
	"github.com/materials-commons/materials"
	"net/http"
	"os"
	"path/filepath"
	"strings"
)

type ProjectResource struct {
	*materials.MaterialsProjects
}

func newProjectResource(container *restful.Container) error {
	p, err := materials.CurrentUserProjects()
	if err != nil {
		return err
	}
	projectResource := ProjectResource{p}
	projectResource.register(container)
	return nil
}

func (p ProjectResource) register(container *restful.Container) {
	ws := new(restful.WebService)
	ws.Path("/projects").
		Consumes(restful.MIME_JSON).
		Produces(restful.MIME_JSON, restful.MIME_XML)

	ws.Route(ws.GET("").Filter(JsonpFilter).To(p.allProjects).
		Doc("list all projects").
		Writes([]materials.Project{}))

	ws.Route(ws.GET("/{project-name}").Filter(JsonpFilter).To(p.getProject).
		Doc("Retrieve a particular project").
		Param(ws.PathParameter("project-name", "name of the project").DataType("string")).
		Writes(materials.Project{}))

	ws.Route(ws.GET("/{project-name}/tree").To(p.getProjectTree).
		Doc("Retrieve the directory/file tree for the project").
		Param(ws.PathParameter("project-name", "name of the project").DataType("string")))

	ws.Route(ws.POST("/projects").To(p.newProject).
		Doc("Create a new project").
		Reads(materials.Project{}))

	container.Add(ws)
}

func (p ProjectResource) allProjects(request *restful.Request, response *restful.Response) {
	if len(p.Projects()) == 0 {
		response.AddHeader("Content-Type", "text/plain")
		response.WriteErrorString(http.StatusNotFound, "User has no projects.")
	} else {
		response.WriteEntity(p.Projects())
	}
}

func (p ProjectResource) getProject(request *restful.Request, response *restful.Response) {
	projectName := request.PathParameter("project-name")
	project, found := p.Find(projectName)
	if found {
		response.WriteEntity(project)
	}

	response.AddHeader("Content-Type", "text/plain")
	response.WriteErrorString(http.StatusNotFound, fmt.Sprintf("Project not found: %s", projectName))
}

func (p ProjectResource) getProjectTree(request *restful.Request, response *restful.Response) {
	type ditem struct {
		Id          string   `json:"id"`
		Name        string   `json:"name"`
		Displayname string   `json:"displayname"`
		Dtype       string   `json:"type"`
		Children    []*ditem `json:"children"`
	}

	dirs := make(map[string]*ditem)
	var currentDir *ditem
	var topLevelDirs []*ditem
	projectName := request.PathParameter("project-name")
	project, found := p.Find(projectName)

	createTopLevelEntry := func(path string) {
		item := &ditem{
			Id:          strings.Replace(path, "/", "_", -1),
			Name:        path,
			Displayname: path,
			Dtype:       "datadir",
			Children:    []*ditem{},
		}

		dirs[path] = item
		currentDir = item
		topLevelDirs = append(topLevelDirs, item)
	}

	// addChild adds a child to the currentDir. If currentDir
	// is different than the childs parent it first updates
	// currentDir to the ditem for the parent path.
	addChild := func(path string, info os.FileInfo) {
		parent := filepath.Dir(path)
		d, found := dirs[parent]

		// There should always be a parent
		if !found {
			panic("d should not be null")
		}

		// Create the ditem
		item := ditem{
			Id:          strings.Replace(strings.Replace(path, "/", "_", -1), ".", "_", -1),
			Name:        path,
			Displayname: filepath.Base(path),
			Children:    []*ditem{},
		}

		// What type of entry is this?
		if info.IsDir() {
			item.Dtype = "datadir"
			dirs[path] = &item
		} else {
			item.Dtype = "datafile"
		}

		// Update the currentDir if needed
		if currentDir.Name != parent {
			currentDir = d
		}

		// Append new entry to the currentDir list of children
		currentDir.Children = append(currentDir.Children, &item)
	}

	if found {
		filepath.Walk(project.Path, func(path string, info os.FileInfo, err error) error {
			if path == project.Path {
				createTopLevelEntry(path)
			} else {
				addChild(path, info)
			}
			return nil
		})

		response.WriteEntity(topLevelDirs)
	} else {
		response.AddHeader("Content-Type", "text/plain")
		response.WriteErrorString(http.StatusNotFound,
			fmt.Sprintf("Project not found: %s", projectName))
	}
}

func (p *ProjectResource) newProject(request *restful.Request, response *restful.Response) {
	project := new(materials.Project)
	err := request.ReadEntity(&project)
	if err != nil {
		response.AddHeader("Content-Type", "text/plain")
		response.WriteErrorString(http.StatusNotAcceptable, err.Error())
	}

	err = p.Add(*project)
	if err != nil {
		response.AddHeader("Content-Type", "text/plain")
		response.WriteErrorString(http.StatusNotAcceptable, err.Error())
	}

	response.WriteHeader(http.StatusCreated)
	response.WriteEntity(project)
}
<commit_msg>Replace multiple calls to strings.Replace with a function for turning paths into an id.<commit_after>package wsmaterials

import (
	"fmt"
	"github.com/emicklei/go-restful"
	"github.com/materials-commons/materials"
	"net/http"
	"os"
	"path/filepath"
	"strings"
)

type ProjectResource struct {
	*materials.MaterialsProjects
}

func newProjectResource(container *restful.Container) error {
	p, err := materials.CurrentUserProjects()
	if err != nil {
		return err
	}
	projectResource := ProjectResource{p}
	projectResource.register(container)
	return nil
}

func (p ProjectResource) register(container *restful.Container) {
	ws := new(restful.WebService)
	ws.Path("/projects").
		Consumes(restful.MIME_JSON).
		Produces(restful.MIME_JSON, restful.MIME_XML)

	ws.Route(ws.GET("").Filter(JsonpFilter).To(p.allProjects).
		Doc("list all projects").
		Writes([]materials.Project{}))

	ws.Route(ws.GET("/{project-name}").Filter(JsonpFilter).To(p.getProject).
		Doc("Retrieve a particular project").
		Param(ws.PathParameter("project-name", "name of the project").DataType("string")).
		Writes(materials.Project{}))

	ws.Route(ws.GET("/{project-name}/tree").To(p.getProjectTree).
		Doc("Retrieve the directory/file tree for the project").
		Param(ws.PathParameter("project-name", "name of the project").DataType("string")))

	ws.Route(ws.POST("/projects").To(p.newProject).
		Doc("Create a new project").
		Reads(materials.Project{}))

	container.Add(ws)
}

func (p ProjectResource) allProjects(request *restful.Request, response *restful.Response) {
	if len(p.Projects()) == 0 {
		response.AddHeader("Content-Type", "text/plain")
		response.WriteErrorString(http.StatusNotFound, "User has no projects.")
	} else {
		response.WriteEntity(p.Projects())
	}
}

func (p ProjectResource) getProject(request *restful.Request, response *restful.Response) {
	projectName := request.PathParameter("project-name")
	project, found := p.Find(projectName)
	if found {
		response.WriteEntity(project)
	}

	response.AddHeader("Content-Type", "text/plain")
	response.WriteErrorString(http.StatusNotFound, fmt.Sprintf("Project not found: %s", projectName))
}

func (p ProjectResource) getProjectTree(request *restful.Request, response *restful.Response) {
	type ditem struct {
		Id          string   `json:"id"`
		Name        string   `json:"name"`
		Displayname string   `json:"displayname"`
		Dtype       string   `json:"type"`
		Children    []*ditem `json:"children"`
	}

	dirs := make(map[string]*ditem)
	var currentDir *ditem
	var topLevelDirs []*ditem
	projectName := request.PathParameter("project-name")
	project, found := p.Find(projectName)

	createTopLevelEntry := func(path string) {
		item := &ditem{
			Id:          path2Id(path),
			Name:        path,
			Displayname: path,
			Dtype:       "datadir",
			Children:    []*ditem{},
		}

		dirs[path] = item
		currentDir = item
		topLevelDirs = append(topLevelDirs, item)
	}

	// addChild adds a child to the currentDir. If currentDir
	// is different than the childs parent it first updates
	// currentDir to the ditem for the parent path.
	addChild := func(path string, info os.FileInfo) {
		parent := filepath.Dir(path)
		d, found := dirs[parent]

		// There should always be a parent
		if !found {
			panic("d should not be null")
		}

		// Create the ditem
		item := ditem{
			Id:          path2Id(path),
			Name:        path,
			Displayname: filepath.Base(path),
			Children:    []*ditem{},
		}

		// What type of entry is this?
		if info.IsDir() {
			item.Dtype = "datadir"
			dirs[path] = &item
		} else {
			item.Dtype = "datafile"
		}

		// Update the currentDir if needed
		if currentDir.Name != parent {
			currentDir = d
		}

		// Append new entry to the currentDir list of children
		currentDir.Children = append(currentDir.Children, &item)
	}

	if found {
		filepath.Walk(project.Path, func(path string, info os.FileInfo, err error) error {
			if path == project.Path {
				createTopLevelEntry(path)
			} else {
				addChild(path, info)
			}
			return nil
		})

		response.WriteEntity(topLevelDirs)
	} else {
		response.AddHeader("Content-Type", "text/plain")
		response.WriteErrorString(http.StatusNotFound,
			fmt.Sprintf("Project not found: %s", projectName))
	}
}

func path2Id(path string) string {
	return strings.Replace(strings.Replace(path, "/", "_", -1), ".", "_", -1)
}

func (p *ProjectResource) newProject(request *restful.Request, response *restful.Response) {
	project := new(materials.Project)
	err := request.ReadEntity(&project)
	if err != nil {
		response.AddHeader("Content-Type", "text/plain")
		response.WriteErrorString(http.StatusNotAcceptable, err.Error())
	}

	err = p.Add(*project)
	if err != nil {
		response.AddHeader("Content-Type", "text/plain")
		response.WriteErrorString(http.StatusNotAcceptable, err.Error())
	}

	response.WriteHeader(http.StatusCreated)
	response.WriteEntity(project)
}
<|endoftext|>


<commit_before>package tree

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

var (
	ErrBadPath = errors.New("invalid path")
)

// superset of Marker
type Node interface {
	Start() (int64, int64) // line, col
	End()   (int64, int64)
	Get(path []interface{}) (Node, error)
	GetMarker() Marker
	pos() []*Pos // just used for iterating through the markers to fill in line and column from index
}

func FixLineColumn(n Node, source []byte) {
	p := n.pos()
	sort.Slice(p, func(i, j int) bool {
		return p[i].Index < p[j].Index
	})
	pi := 0
	line, col := int64(1), int64(1)
	for i, c := range source {
		if int64(i) == p[pi].Index {
			p[pi].Line = line
			p[pi].Column = col
			pi++
		}
		col++
		if c == '\n' {
			line++
			col = 1
		}
	}
}

// Key is used to differentiate leaves describing the start and end of where
// a key starts and where a value starts.
type Key string

type Pos struct {
	Index  int64
	Line   int64
	Column int64
}

func posString(p *Pos) string {
	if p == nil {
		return ""
	}
	return fmt.Sprintf("line %d col %d", p.Line, p.Column)
}

func posLC(p *Pos) (int64, int64) {
	if p == nil {
		return 0, 0
	}
	return p.Line, p.Column
}

// IndexMarkers are composed of information regarding the start and
// end of where a Node exists in its source. Depending on where the marker
// came from (e.g. json or yaml) the *Idx or End* fields may not be populated.
type Marker struct {
	StartP *Pos
	EndP   *Pos
}

func (m Marker) Start() (int64, int64) {
	return posLC(m.StartP)
}

func (m Marker) End() (int64, int64) {
	return posLC(m.EndP)
}

func (m Marker) String() string {
	// Just do start for now, figure out end later
	return posString(m.StartP)
}

func (marker Marker) GetMarker() Marker {
	return marker
}

func MarkerFromIndices(start, end int64) Marker {
	return Marker{
		StartP: &Pos{Index: start},
		EndP:   &Pos{Index: end},
	}
}

func appendPos(l []*Pos, p *Pos) []*Pos {
	if p != nil {
		return append(l, p)
	}
	return l
}

type MapNode struct {
	Marker
	Children map[string]Node
	Keys     map[string]Leaf
}

func (m MapNode) Get(path []interface{}) (Node, error) {
	if len(path) == 0 {
		return m, nil
	}
	switch p := path[0].(type) {
	case string:
		if r, ok := m.Children[p]; ok {
			return r.Get(path[1:])
		} else {
			return nil, ErrBadPath
		}
	case Key:
		if r, ok := m.Keys[string(p)]; ok {
			return r.Get(path[1:])
		} else {
			return nil, ErrBadPath
		}
	default:
		return nil, ErrBadPath
	}
}

func (m MapNode) pos() []*Pos {
	ret := appendPos(nil, m.StartP)
	for _, v := range m.Children {
		ret = append(ret, v.pos()...)
	}
	for _, v := range m.Keys {
		ret = append(ret, v.pos()...)
	}
	ret = appendPos(ret, m.EndP)
	return ret
}

type Leaf struct {
	Marker
}

func (l Leaf) pos() []*Pos {
	return appendPos(appendPos(nil, l.StartP), l.EndP)
}

func (k Leaf) Get(path []interface{}) (Node, error) {
	if len(path) == 0 {
		return k, nil
	}
	return nil, ErrBadPath
}

type SliceNode struct {
	Marker
	Children []Node
}

func (s SliceNode) Get(path []interface{}) (Node, error) {
	if len(path) == 0 {
		return s, nil
	}
	if i, ok := path[0].(int); ok {
		if i >= len(s.Children) {
			return nil, ErrBadPath
		}
		return s.Children[i].Get(path[1:])
	}
	return nil, ErrBadPath
}

func (s SliceNode) pos() []*Pos {
	ret := appendPos(nil, s.StartP)
	for _, v := range s.Children {
		ret = append(ret, v.pos()...)
	}
	ret = appendPos(ret, s.EndP)
	return ret
}
<commit_msg>tree: don't add negative offsets, fix index oob<commit_after>package tree

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

var (
	ErrBadPath = errors.New("invalid path")
)

// superset of Marker
type Node interface {
	Start() (int64, int64) // line, col
	End()   (int64, int64)
	Get(path []interface{}) (Node, error)
	GetMarker() Marker
	pos() []*Pos // just used for iterating through the markers to fill in line and column from index
}

func FixLineColumn(n Node, source []byte) {
	p := n.pos()
	sort.Slice(p, func(i, j int) bool {
		return p[i].Index < p[j].Index
	})
	pi := 0
	line, col := int64(1), int64(1)
	for i, c := range source {
		if pi == len(p) {
			return
		}
		if int64(i) == p[pi].Index {
			p[pi].Line = line
			p[pi].Column = col
			pi++
		}
		col++
		if c == '\n' {
			line++
			col = 1
		}
	}
}

// Key is used to differentiate leaves describing the start and end of where
// a key starts and where a value starts.
type Key string

type Pos struct {
	Index  int64
	Line   int64
	Column int64
}

func posString(p *Pos) string {
	if p == nil {
		return ""
	}
	return fmt.Sprintf("line %d col %d", p.Line, p.Column)
}

func posLC(p *Pos) (int64, int64) {
	if p == nil {
		return 0, 0
	}
	return p.Line, p.Column
}

// IndexMarkers are composed of information regarding the start and
// end of where a Node exists in its source. Depending on where the marker
// came from (e.g. json or yaml) the *Idx or End* fields may not be populated.
type Marker struct {
	StartP *Pos
	EndP   *Pos
}

func (m Marker) Start() (int64, int64) {
	return posLC(m.StartP)
}

func (m Marker) End() (int64, int64) {
	return posLC(m.EndP)
}

func (m Marker) String() string {
	// Just do start for now, figure out end later
	return posString(m.StartP)
}

func (marker Marker) GetMarker() Marker {
	return marker
}

func MarkerFromIndices(start, end int64) Marker {
	m := Marker{}
	if start >= 0 {
		m.StartP = &Pos{Index: start}
	}
	if end >= 0 {
		m.EndP = &Pos{Index: end}
	}
	return m
}

func appendPos(l []*Pos, p *Pos) []*Pos {
	if p != nil {
		return append(l, p)
	}
	return l
}

type MapNode struct {
	Marker
	Children map[string]Node
	Keys     map[string]Leaf
}

func (m MapNode) Get(path []interface{}) (Node, error) {
	if len(path) == 0 {
		return m, nil
	}
	switch p := path[0].(type) {
	case string:
		if r, ok := m.Children[p]; ok {
			return r.Get(path[1:])
		} else {
			return nil, ErrBadPath
		}
	case Key:
		if r, ok := m.Keys[string(p)]; ok {
			return r.Get(path[1:])
		} else {
			return nil, ErrBadPath
		}
	default:
		return nil, ErrBadPath
	}
}

func (m MapNode) pos() []*Pos {
	ret := appendPos(nil, m.StartP)
	for _, v := range m.Children {
		ret = append(ret, v.pos()...)
	}
	for _, v := range m.Keys {
		ret = append(ret, v.pos()...)
	}
	ret = appendPos(ret, m.EndP)
	return ret
}

type Leaf struct {
	Marker
}

func (l Leaf) pos() []*Pos {
	return appendPos(appendPos(nil, l.StartP), l.EndP)
}

func (k Leaf) Get(path []interface{}) (Node, error) {
	if len(path) == 0 {
		return k, nil
	}
	return nil, ErrBadPath
}

type SliceNode struct {
	Marker
	Children []Node
}

func (s SliceNode) Get(path []interface{}) (Node, error) {
	if len(path) == 0 {
		return s, nil
	}
	if i, ok := path[0].(int); ok {
		if i >= len(s.Children) {
			return nil, ErrBadPath
		}
		return s.Children[i].Get(path[1:])
	}
	return nil, ErrBadPath
}

func (s SliceNode) pos() []*Pos {
	ret := appendPos(nil, s.StartP)
	for _, v := range s.Children {
		ret = append(ret, v.pos()...)
	}
	ret = appendPos(ret, s.EndP)
	return ret
}
<|endoftext|>


<commit_before>package tree

import (
	"fmt"
	"io/ioutil"
	"log"
	"strings"
	"testing"
)

func ExampleTree() {
	file, err := ioutil.ReadFile("files.txt")
	if err != nil {
		log.Fatal(err)
	}

	g := New("/")
	lines := strings.Split(string(file), "\n")

	g.EatLines(lines)

	fmt.Print(g.Format())
	// Output:
	// .
	// ├── 1
	// │   ├── 2
	// │   │   └── 3
	// │   │       ├── 4
	// │   │       │   ├── 5
	// │   │       │   └── fisk2.txt
	// │   │       └── fisk2.txt
	// │   ├── 3
	// │   │   ├── 4
	// │   │   │   ├── 5
	// │   │   │   └── fisk2.txt
	// │   │   └── fisk2.txt
	// │   ├── 5
	// │   │   ├── 4
	// │   │   │   ├── 3
	// │   │   │   │   ├── 2
	// │   │   │   │   └── fisk.txt
	// │   │   │   └── fisk.txt
	// │   │   ├── fisk.txt
	// │   │   └── fisk2.txt
	// │   └── fisk.txt
	// └── fisk.txt
	//
}

func TestShallowTree(t *testing.T) {
	lines := []string{
		"one",
		"other",
		"this",
	}

	expected := `.
├── one
├── other
└── this
`
	tr := New("/")
	tr.EatLines(lines)

	output := tr.Format()

	errorFormat := `Expected
===
%s===

Got
===
%s===`

	if output != expected {
		t.Error("fisk...")
		t.Errorf(
			errorFormat,
			expected,
			output,
		)
	}
}

func TestNodeFormat(t *testing.T) {
	lines := []string{
		"one",
		"other$retho",
		"this",
	}

	expected := `.
├── ✓ one ⚡
├── ✓ other ⚡
│   └── ✓ retho ⚡
└── ✓ this ⚡
`
	tr := New("$")
	tr.NodeFormat = "✓ %s ⚡"
	tr.EatLines(lines)

	output := tr.Format()

	errorFormat := `Expected
===
%s===

Got
===
%s===`

	if output != expected {
		t.Error("fisk...")
		t.Errorf(
			errorFormat,
			expected,
			output,
		)
	}
}
<commit_msg>Add benchmark<commit_after>package tree

import (
	"fmt"
	"io/ioutil"
	"log"
	"strings"
	"testing"
)

func ExampleTree() {
	file, err := ioutil.ReadFile("files.txt")
	if err != nil {
		log.Fatal(err)
	}

	g := New("/")
	lines := strings.Split(string(file), "\n")

	g.EatLines(lines)

	fmt.Print(g.Format())
	// Output:
	// .
	// ├── 1
	// │   ├── 2
	// │   │   └── 3
	// │   │       ├── 4
	// │   │       │   ├── 5
	// │   │       │   └── fisk2.txt
	// │   │       └── fisk2.txt
	// │   ├── 3
	// │   │   ├── 4
	// │   │   │   ├── 5
	// │   │   │   └── fisk2.txt
	// │   │   └── fisk2.txt
	// │   ├── 5
	// │   │   ├── 4
	// │   │   │   ├── 3
	// │   │   │   │   ├── 2
	// │   │   │   │   └── fisk.txt
	// │   │   │   └── fisk.txt
	// │   │   ├── fisk.txt
	// │   │   └── fisk2.txt
	// │   └── fisk.txt
	// └── fisk.txt
	//
}

func TestShallowTree(t *testing.T) {
	lines := []string{
		"one",
		"other",
		"this",
	}

	expected := `.
├── one
├── other
└── this
`
	tr := New("/")
	tr.EatLines(lines)

	output := tr.Format()

	errorFormat := `Expected
===
%s===

Got
===
%s===`

	if output != expected {
		t.Error("fisk...")
		t.Errorf(
			errorFormat,
			expected,
			output,
		)
	}
}

func TestNodeFormat(t *testing.T) {
	lines := []string{
		"one",
		"other$retho",
		"this",
	}

	expected := `.
├── ✓ one ⚡
├── ✓ other ⚡
│   └── ✓ retho ⚡
└── ✓ this ⚡
`
	tr := New("$")
	tr.NodeFormat = "✓ %s ⚡"
	tr.EatLines(lines)

	output := tr.Format()

	errorFormat := `Expected
===
%s===

Got
===
%s===`

	if output != expected {
		t.Error("fisk...")
		t.Errorf(
			errorFormat,
			expected,
			output,
		)
	}
}

func BenchmarkTreeFormat(b *testing.B) {
	file, err := ioutil.ReadFile("files.txt")
	lines := strings.Split(string(file), "\n")
	if nil != err {
		log.Fatal(err)
	}

	for i := 0; i < b.N; i++ {
		t := New("/")
		t.EatLines(lines)
		t.Format()
	}
}
<|endoftext|>


<commit_before>/*
@author: xuchengxuan(bigpyer@126.com)
@brief: logger module
*/
package ymtlog

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"sync"
	"time"
)

const (
	DATEFORMAT        = "2006-01-02"
	DEFAULT_LOG_SCAN  = 300
	DEFAULT_LOG_LEVEL = ERROR
)

type LEVEL byte

const (
	INFO LEVEL = iota
	DEBUG
	WARN
	ERROR
)

type logger struct {
	mu       *sync.RWMutex
	fileDir  string
	fileName string

	date *time.Time

	logFile  *os.File
	lger     *log.Logger
	timeScan int64

	logChan  chan string
	logLevel LEVEL
}

// logger handler constructor
func NewLogger(dir string, name string) *logger {
	dailyLogger := &logger{
		mu:       new(sync.RWMutex),
		fileDir:  dir,
		fileName: name,
		logChan:  make(chan string, 500),
		logLevel: DEFAULT_LOG_LEVEL,
	}

	dailyLogger.initDailyLogger()

	return dailyLogger
}

/*
* @param level(0,1,2,3)
* @desc  if output level is larger than log level,
*       the content will be outputed
 */
func (f *logger) SetLogLevel(level LEVEL) {
	f.logLevel = level
}

func (f *logger) initDailyLogger() {

	t, _ := time.Parse(DATEFORMAT, time.Now().Format(DATEFORMAT))

	f.date = &t
	f.mu.Lock()
	defer f.mu.Unlock()

	logFile := joinFilePath(f.fileDir, f.fileName)
	f.logFile, _ = os.OpenFile(logFile, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0666)
	f.lger = log.New(f.logFile, "", log.LstdFlags|log.Lmicroseconds)

	go f.writeLog()
	go f.monitorFile()
}

func (f *logger) isNeedRotate() bool {
	t, _ := time.Parse(DATEFORMAT, time.Now().Format(DATEFORMAT))
	if t.After(*f.date) {
		return true
	}
	return false
}

func (f *logger) rotate() {
	logFile := joinFilePath(f.fileDir, f.fileName)
	logFileBak := logFile + "." + f.date.Format(DATEFORMAT)

	if !isExist(logFileBak) && f.isNeedRotate() {
		if f.logFile != nil {
			f.logFile.Close()
		}

		err := os.Rename(logFile, logFileBak)
		if err != nil {
			f.lger.Printf("logger rename error: %v", err.Error())
		}

		t, _ := time.Parse(DATEFORMAT, time.Now().Format(DATEFORMAT))
		f.date = &t
		f.logFile, _ = os.Create(logFile)
		f.lger = log.New(f.logFile, "", log.LstdFlags|log.Lmicroseconds)
	}
}

func (f *logger) monitorFile() {
	defer func() {
		if err := recover(); err != nil {
			f.lger.Panic("logger's FileMonitor() catch panic: %v\n", err)
		}
	}()

	// check frequency
	logScan := DEFAULT_LOG_SCAN

	timer := time.NewTicker(time.Duration(logScan) * time.Second)
	for {
		select {
		case <-timer.C:
			f.checkFile()
		}
	}
}

func (f *logger) checkFile() {
	defer func() {
		if err := recover(); err != nil {
			f.lger.Printf("logger's FileCheck() catch panic: %v\n", err)
		}
	}()
	if f.isNeedRotate() {
		f.mu.Lock()
		defer f.mu.Unlock()

		f.rotate()
	}
}

// passive to close filelogger
func (f *logger) Close() error {

	close(f.logChan)
	f.lger = nil

	return f.logFile.Close()
}

// Receive logStr from f's logChan and print logstr to file
func (f *logger) writeLog() {
	defer func() {
		if err := recover(); err != nil {
			log.Printf(" writeLog catch panic: %v\n", err)
		}
	}()

	for {
		select {
		case str := <-f.logChan:
			f.outPut(str)
		}
	}
}

// print log
func (f *logger) outPut(str string) {
	f.mu.RLock()
	defer f.mu.RUnlock()

	f.lger.Output(2, str)
}

func isExist(path string) bool {
	_, err := os.Stat(path)
	return err == nil || os.IsExist(err)
}

func joinFilePath(path, file string) string {
	return filepath.Join(path, file)
}

func shortFileName(file string) string {
	return filepath.Base(file)
}

// info log
func (f *logger) Info(format string, v ...interface{}) {
	_, file, line, _ := runtime.Caller(1) //calldepth=1
	if f.logLevel <= INFO {
		f.logChan <- fmt.Sprintf("[%v:%v]", shortFileName(file), line) + fmt.Sprintf("[INFO] "+format, v...)
	}
}

// debug log
func (f *logger) Debug(format string, v ...interface{}) {
	_, file, line, _ := runtime.Caller(1) //calldepth=1
	if f.logLevel <= DEBUG {
		f.logChan <- fmt.Sprintf("[%v:%v]", shortFileName(file), line) + fmt.Sprintf("[DEBUG] "+format, v...)
	}
}

// warn log
func (f *logger) Warn(format string, v ...interface{}) {
	_, file, line, _ := runtime.Caller(1) //calldepth=1
	if f.logLevel <= WARN {
		f.logChan <- fmt.Sprintf("[%v:%v]", shortFileName(file), line) + fmt.Sprintf("[WARN] "+format, v...)
	}
}

// error log
func (f *logger) Error(format string, v ...interface{}) {
	_, file, line, _ := runtime.Caller(1) //calldepth=1
	if f.logLevel <= ERROR {
		f.logChan <- fmt.Sprintf("[%v:%v]", shortFileName(file), line) + fmt.Sprintf("[ERROR] "+format, v...)
	}
}
<commit_msg>rename  ymtlog to alog<commit_after>/*
@author: xuchengxuan(bigpyer@126.com)
@brief: logger module
*/
package alog

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"sync"
	"time"
)

const (
	DATEFORMAT        = "2006-01-02"
	DEFAULT_LOG_SCAN  = 300
	DEFAULT_LOG_LEVEL = ERROR
)

type LEVEL byte

const (
	INFO LEVEL = iota
	DEBUG
	WARN
	ERROR
)

type logger struct {
	mu       *sync.RWMutex
	fileDir  string
	fileName string

	date *time.Time

	logFile  *os.File
	lger     *log.Logger
	timeScan int64

	logChan  chan string
	logLevel LEVEL
}

// logger handler constructor
func NewLogger(dir string, name string) *logger {
	dailyLogger := &logger{
		mu:       new(sync.RWMutex),
		fileDir:  dir,
		fileName: name,
		logChan:  make(chan string, 500),
		logLevel: DEFAULT_LOG_LEVEL,
	}

	dailyLogger.initDailyLogger()

	return dailyLogger
}

/*
* @param level(0,1,2,3)
* @desc  if output level is larger than log level,
*       the content will be outputed
 */
func (f *logger) SetLogLevel(level LEVEL) {
	f.logLevel = level
}

func (f *logger) initDailyLogger() {

	t, _ := time.Parse(DATEFORMAT, time.Now().Format(DATEFORMAT))

	f.date = &t
	f.mu.Lock()
	defer f.mu.Unlock()

	logFile := joinFilePath(f.fileDir, f.fileName)
	f.logFile, _ = os.OpenFile(logFile, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0666)
	f.lger = log.New(f.logFile, "", log.LstdFlags|log.Lmicroseconds)

	go f.writeLog()
	go f.monitorFile()
}

func (f *logger) isNeedRotate() bool {
	t, _ := time.Parse(DATEFORMAT, time.Now().Format(DATEFORMAT))
	if t.After(*f.date) {
		return true
	}
	return false
}

func (f *logger) rotate() {
	logFile := joinFilePath(f.fileDir, f.fileName)
	logFileBak := logFile + "." + f.date.Format(DATEFORMAT)

	if !isExist(logFileBak) && f.isNeedRotate() {
		if f.logFile != nil {
			f.logFile.Close()
		}

		err := os.Rename(logFile, logFileBak)
		if err != nil {
			f.lger.Printf("logger rename error: %v", err.Error())
		}

		t, _ := time.Parse(DATEFORMAT, time.Now().Format(DATEFORMAT))
		f.date = &t
		f.logFile, _ = os.Create(logFile)
		f.lger = log.New(f.logFile, "", log.LstdFlags|log.Lmicroseconds)
	}
}

func (f *logger) monitorFile() {
	defer func() {
		if err := recover(); err != nil {
			f.lger.Panic("logger's FileMonitor() catch panic: %v\n", err)
		}
	}()

	// check frequency
	logScan := DEFAULT_LOG_SCAN

	timer := time.NewTicker(time.Duration(logScan) * time.Second)
	for {
		select {
		case <-timer.C:
			f.checkFile()
		}
	}
}

func (f *logger) checkFile() {
	defer func() {
		if err := recover(); err != nil {
			f.lger.Printf("logger's FileCheck() catch panic: %v\n", err)
		}
	}()
	if f.isNeedRotate() {
		f.mu.Lock()
		defer f.mu.Unlock()

		f.rotate()
	}
}

// passive to close filelogger
func (f *logger) Close() error {

	close(f.logChan)
	f.lger = nil

	return f.logFile.Close()
}

// Receive logStr from f's logChan and print logstr to file
func (f *logger) writeLog() {
	defer func() {
		if err := recover(); err != nil {
			log.Printf(" writeLog catch panic: %v\n", err)
		}
	}()

	for {
		select {
		case str := <-f.logChan:
			f.outPut(str)
		}
	}
}

// print log
func (f *logger) outPut(str string) {
	f.mu.RLock()
	defer f.mu.RUnlock()

	f.lger.Output(2, str)
}

func isExist(path string) bool {
	_, err := os.Stat(path)
	return err == nil || os.IsExist(err)
}

func joinFilePath(path, file string) string {
	return filepath.Join(path, file)
}

func shortFileName(file string) string {
	return filepath.Base(file)
}

// info log
func (f *logger) Info(format string, v ...interface{}) {
	_, file, line, _ := runtime.Caller(1) //calldepth=1
	if f.logLevel <= INFO {
		f.logChan <- fmt.Sprintf("[%v:%v]", shortFileName(file), line) + fmt.Sprintf("[INFO] "+format, v...)
	}
}

// debug log
func (f *logger) Debug(format string, v ...interface{}) {
	_, file, line, _ := runtime.Caller(1) //calldepth=1
	if f.logLevel <= DEBUG {
		f.logChan <- fmt.Sprintf("[%v:%v]", shortFileName(file), line) + fmt.Sprintf("[DEBUG] "+format, v...)
	}
}

// warn log
func (f *logger) Warn(format string, v ...interface{}) {
	_, file, line, _ := runtime.Caller(1) //calldepth=1
	if f.logLevel <= WARN {
		f.logChan <- fmt.Sprintf("[%v:%v]", shortFileName(file), line) + fmt.Sprintf("[WARN] "+format, v...)
	}
}

// error log
func (f *logger) Error(format string, v ...interface{}) {
	_, file, line, _ := runtime.Caller(1) //calldepth=1
	if f.logLevel <= ERROR {
		f.logChan <- fmt.Sprintf("[%v:%v]", shortFileName(file), line) + fmt.Sprintf("[ERROR] "+format, v...)
	}
}
<|endoftext|>


<commit_before>package main

import (
	`bytes`
	`fmt`
	`io/ioutil`
	`regexp`
        `os`
)

type Card struct {
	rank  rune
	suit  rune
}

type Code []Card

type Ante struct {
        pc      int
        line    int
        code    []Card
        vars    map[rune]int
        labels  map[rune]int
}

func (ante *Ante)Initialize() *Ante {
        ante.code = make(Code, 0)
        ante.vars = map[rune]int{ '♦': 0, '♥': 0, '♠': 0, '♣': 0}
        ante.labels = map[rune]int{}

        return ante
}

func (ante *Ante)Run(filename string) *Ante {
        program, err := ioutil.ReadFile(filename)
        if err != nil {
                ante.exception(fmt.Sprintf("%q", err))
        }

        ante.parse(program)

        for ante.pc < len(ante.code) {
                card := ante.code[ante.pc]
                ante.pc++
                switch card.rank {
                case 0  : ante.newline(card)
                case 'K': ante.jump(card)
                case 'Q': continue
                case 'J': ante.dump(card, true)
                case 10 : ante.dump(card, false)
                default : ante.assign(card)
                }
        }

        return ante
}

func (ante *Ante)parse(program []byte) *Ante {
        fmt.Println("parse...")
        // Split program blob into lines.
        lines := bytes.Split(program, []byte("\n"))

        // Get rid of comments and whitespaces.
        comments := regexp.MustCompile(`#.*$`)
        whitespace := regexp.MustCompile(`^\s+|\s+$`)
        for i := 0; i < len(lines); i++ {
                lines[i] = comments.ReplaceAllLiteral(lines[i], []byte(``))
                lines[i] = whitespace.ReplaceAllLiteral(lines[i], []byte(``))
                fmt.Printf("[%s]\n", lines[i])
        }

        // Turn source file into array of cards. Each card is 2-item
        // struct of rank and suit.
        re := regexp.MustCompile(`(10|[2-9JQKA])([♦♥♠♣])`)
        for i := 0; i < len(lines); i++ {
                // Line number cards have 0 rank.
                ante.code = append(ante.code, Card{ 0, rune(i + 1) })
                cards := re.FindAllSubmatch(lines[i], -1)
                for _, c := range(cards) {
                        ante.code = append(ante.code, Card{ bytes.Runes(c[1])[0], bytes.Runes(c[2])[0]})
                }
        }
        //fmt.Printf("\n%q\n", ante.code)

        // A pass to extract labels.
        for pc := 0; pc < len(ante.code); {
                card := ante.code[pc]
                pc++
                if card.rank == '1' {
                        ante.code[pc-1].rank = 10
                } else if card.rank >= '2' && card.rank <= '9' {
                        ante.code[pc-1].rank = card.rank - '0'
                } else if card.rank == 'Q' {
                        queen := card.suit
                        for pc < len(ante.code) && ante.code[pc].rank == 'Q' && ante.code[pc].suit == card.suit {
                                queen += card.suit
                                pc++
                        }
                        ante.labels[queen] = pc
                }
        }
        fmt.Printf("\n%q\n", ante.code)
        fmt.Printf("%q\n", ante.labels)
        return ante
}

func (ante *Ante)newline(card Card) *Ante {
        ante.line++
        return ante
}

// # puts "jump #{card.inspect}, pc: #{@pc.inspect}, #{@labels.inspect}"
// suit = card.suit
// while @code[@pc] && @code[@pc].rank == "K" && @code[@pc].suit == card.suit
//   suit += card.suit
//   @pc += 1
// end
//
// if instance_variable_get("@#{suit[0]}") != 0
//   # puts "jumping to " << "Q#{suit[0]}" * suit.size
//   if @labels[suit]
//     @pc = @labels[suit]
//   else
//     exception("can't find " << "Q#{suit[0]}" * suit.size << " to go to")
//   end
// end

func (ante *Ante)jump(card Card) *Ante {
        //fmt.Printf(`jump: %q, %d, %q\n`, card, ante.pc, ante.labels)
        suit := card.suit
        for ante.pc < len(ante.code) && ante.code[ante.pc].rank == 'K' && ante.code[ante.pc].suit == card.suit {
                suit += card.suit
                ante.pc++
        }
        if ante.vars[card.suit] != 0 {
                if _, ok := ante.labels[suit]; ok {
                        ante.pc = ante.labels[suit]
                } else {
                        ante.exception(`can't find Q? to go`)
                }
        }

        return ante
}

func (ante *Ante)dump(card Card, char bool) *Ante {
        //fmt.Printf("dump %q\n", char)
        value := ante.vars[card.suit]
        if char {
                if value < 0 || value > 255 {
                        ante.exception(fmt.Sprintf(`character code %d is out of 0..255 range`, value))
                } else {
                        fmt.Printf(`%c`, value)
                }
        } else {
                fmt.Print(value)
        }
        return ante
}

func (ante *Ante)assign(card Card) *Ante {
        //fmt.Printf("assign %q\n", card)
        operands := ante.remaining(card)
        return ante.expression(operands)
}

// Fetch the rest of the assignment expression.
func (ante *Ante)remaining(card Card) []Card {
        operands := []Card{ card }
        for ante.pc < len(ante.code) {
                card = ante.code[ante.pc]
                if card.rank == 0 || card.rank == 'K' || card.rank == 'Q' || card.rank == 'J' {
                        break
                }
                operands = append(operands, card)
                ante.pc++
        }

        //fmt.Printf("remaining: %q\n", operands)
        return operands
}

func (ante *Ante)expression(operands []Card) *Ante {
        initial, target := int(operands[0].rank), operands[0].suit
        //fmt.Printf("expression initial: %d (%q)\n", initial, operands[0])

        if initial == 'A' {
                initial = ante.vars[target]
        }

        for _, card := range(operands[1:]) {
                rank, suit := int(card.rank), card.suit
                //fmt.Printf("expression rank: %d, %c\n", rank, suit)
                if rank == 'A' {
                        rank = ante.vars[suit]
                        //fmt.Printf("expression rAnk: %d, %c\n", rank, suit)
                }
                switch suit {
                case '♦': initial += rank
                case '♥': initial *= rank
                case '♠': initial -= rank
                case '♣':
                        if rank != 0 {
                                initial /= rank
                        } else {
                                ante.exception(`division by zero`)
                        }
                }
                //fmt.Printf("initial: %d\n", initial)
        }
        ante.vars[target] = initial
        //fmt.Printf("expression => %c: %d\n", target, initial)
        return ante
}

func (ante *Ante)exception(message string) {
        fmt.Printf("Ante exception: %s on line %d (pc:%d)\n", message, ante.line, ante.pc)
        os.Exit(1)
}

func main() {
        if len(os.Args) == 2 {
                new(Ante).Initialize().Run(os.Args[1])
        } else {
                fmt.Println(`usage: ante filename.ante`)
        }
}
<commit_msg>Completed Ante implementation in Go (utf-8 and big.Int). Yusuke's quine.ante passes the test<commit_after>package main

import (
	`bytes`
	`fmt`
	`io/ioutil`
	`math/big`
	`os`
	`regexp`
	`strings`
	`unicode/utf8`
)

type Card struct {
	rank rune
	suit rune
}

type Ante struct {
	pc     int               // Program counter (index within ante.code)
	line   int               // Current line number.
	code   []Card            // Array of cards.
	vars   map[rune]*big.Int // Four registers hashed by suit.
	labels map[rune]int      // Labels for ante.pc to jump to.
	buffer []byte            // Buffer to collect UTF-8 character bytes.
}

func (ante *Ante) Initialize() *Ante {
	ante.labels = map[rune]int{}
	ante.vars = map[rune]*big.Int{
		'♦': big.NewInt(0),
		'♥': big.NewInt(0),
		'♠': big.NewInt(0),
		'♣': big.NewInt(0),
	}

	return ante
}

func (ante *Ante) Run(filename string) *Ante {
	program, err := ioutil.ReadFile(filename)
	if err != nil {
		ante.exception(fmt.Sprintf(`%q`, err))
	}

	ante.parse(program)

	for ante.pc < len(ante.code) {
		card := ante.code[ante.pc]
		ante.pc++

		switch card.rank {
		case 0:
			ante.newline(card)
		case 'K':
			ante.jump(card)
		case 'Q':
			continue
		case 'J':
			ante.dump(card, true)
		case 10:
			ante.dump(card, false)
		default:
			ante.assign(card)
		}
	}

	return ante
}

func (ante *Ante) parse(program []byte) *Ante {
	// Split program blob into lines.
	lines := bytes.Split(program, []byte("\n"))

	// Get rid of comments and whitespaces.
	comments := regexp.MustCompile(`#.*$`)
	whitespace := regexp.MustCompile(`^\s+|\s+$`)
	for i := 0; i < len(lines); i++ {
		lines[i] = comments.ReplaceAllLiteral(lines[i], []byte(``))
		lines[i] = whitespace.ReplaceAllLiteral(lines[i], []byte(``))
		//fmt.Printf("[%s]\n", lines[i])
	}

	// Turn source file into array of cards.
	re := regexp.MustCompile(`(10|[2-9JQKA])([♦♥♠♣])`)
	for i := 0; i < len(lines); i++ {
		// Line number cards have 0 rank.
		ante.code = append(ante.code, Card{0, rune(i + 1)})
		cards := re.FindAllSubmatch(lines[i], -1)
		for _, c := range cards {
			ante.code = append(ante.code, Card{bytes.Runes(c[1])[0], bytes.Runes(c[2])[0]})
		}
	}

	// A pass to convert ranks to integers and extract labels.
	for pc := 0; pc < len(ante.code); {
		card := ante.code[pc]
		pc++
		if card.rank == '1' {
			ante.code[pc-1].rank = 10
		} else if card.rank >= '2' && card.rank <= '9' {
			ante.code[pc-1].rank = card.rank - '0'
		} else if card.rank == 'Q' {
			queen := card.suit
			for pc < len(ante.code) && ante.code[pc].rank == 'Q' && ante.code[pc].suit == card.suit {
				queen += card.suit
				pc++
			}
			ante.labels[queen] = pc
		}
	}
	//fmt.Printf("%q\n", ante.code)
	//fmt.Printf("%q\n", ante.labels)

	return ante
}

func (ante *Ante) newline(card Card) *Ante {
	ante.line++

	return ante
}

func (ante *Ante) jump(card Card) *Ante {
	//fmt.Printf("jump: %q, %d, %q\n", card, ante.pc, ante.labels)
	suit := card.suit
	for ante.pc < len(ante.code) && ante.code[ante.pc].rank == 'K' && ante.code[ante.pc].suit == card.suit {
		suit += card.suit
		ante.pc++
	}

	// if ante.vars[card.suit] != 0 ...
	if ante.vars[card.suit].Cmp(big.NewInt(0)) != 0 {
		if _, ok := ante.labels[suit]; ok {
			ante.pc = ante.labels[suit]
		} else {
			label := strings.Repeat(fmt.Sprintf(`Q%c`, card.suit), int(suit/card.suit))
			ante.exception(`can't find ` + label + ` to go`)
		}
	}

	return ante
}

func (ante *Ante) dump(card Card, char bool) *Ante {
	//fmt.Printf("dump %q\n", char)
	value := ante.vars[card.suit]
	if char {
		// if value < 0 || value > 255 ...
		if value.Cmp(big.NewInt(0)) == -1 || value.Cmp(big.NewInt(255)) == 1 {
			ante.exception(fmt.Sprintf(`character code %d is out of 0..255 range`, value))
		} else {
			// Collect the bytes till we have full UTF-8 character.
			// Once the character is built dump it and reset the buffer.
			ante.buffer = append(ante.buffer, byte(value.Int64()))
			if utf8.FullRune(ante.buffer) {
				fmt.Printf(`%s`, ante.buffer)
				ante.buffer = []byte{}
			}
		}
	} else {
		fmt.Print(value)
	}

	return ante
}

func (ante *Ante) assign(card Card) *Ante {
	//fmt.Printf("assign %q\n", card)
	operands := ante.remaining(card)

	return ante.expression(operands)
}

// Fetch the rest of the assignment expression.
func (ante *Ante) remaining(card Card) []Card {
	operands := []Card{card}
	for ante.pc < len(ante.code) {
		card = ante.code[ante.pc]
		if card.rank == 0 || card.rank == 'K' || card.rank == 'Q' || card.rank == 'J' {
			break
		}
		operands = append(operands, card)
		ante.pc++
	}

	//fmt.Printf("remaining: %q\n", operands)
	return operands
}

func (ante *Ante) expression(operands []Card) *Ante {
	initial := big.NewInt(int64(operands[0].rank))
	target := operands[0].suit

	// if initial == 'A' ...
	if initial.Cmp(big.NewInt('A')) == 0 {
		// initial = ante.vars[target] // <-- Wrong!!! We need local copy, not a pointer to register.
		initial.Set(ante.vars[target]) // <-- The right way to interpolate.
	}

	// Go through remaining operands.
	for _, card := range operands[1:] {
		rank := big.NewInt(int64(card.rank))
		suit := card.suit
		//fmt.Printf("rank: %d, suit: %c\n", rank, suit)

		// if rank == 'A' ...
		if rank.Cmp(big.NewInt('A')) == 0 {
			rank.Set(ante.vars[suit]) // <-- The right way to interpolate.
		}
		switch suit {
		case '♦':
			initial.Add(initial, rank)
		case '♥':
			initial.Mul(initial, rank)
		case '♠':
			initial.Sub(initial, rank)
		case '♣':
			if rank.Cmp(big.NewInt(0)) != 0 {
				initial.Div(initial, rank)
			} else {
				ante.exception(`division by zero`)
			}
		}
	}
	ante.vars[target] = initial
	//fmt.Printf("  %d %c => %d\n", ante.pc, target, initial)

	return ante
}

func (ante *Ante) exception(message string) {
	fmt.Printf("Ante exception: %s on line %d (pc:%d)\n", message, ante.line, ante.pc)
	os.Exit(1)
}

func main() {
	if len(os.Args) == 2 {
		new(Ante).Initialize().Run(os.Args[1])
	} else {
		fmt.Println(`usage: ante filename.ante`)
	}
}
<|endoftext|>