<commit_before>// Copyright (c) 2014 Ashley Jeffs
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package writer

import (
	"strings"
	"time"

	"github.com/Jeffail/benthos/lib/types"
	"github.com/Jeffail/benthos/lib/util/service/log"
	"github.com/Jeffail/benthos/lib/util/service/metrics"
	"github.com/Jeffail/benthos/lib/util/text"
	"github.com/Shopify/sarama"
)

//------------------------------------------------------------------------------

// KafkaConfig is configuration for the Kafka output type.
type KafkaConfig struct {
	Addresses   []string `json:"addresses" yaml:"addresses"`
	ClientID    string   `json:"client_id" yaml:"client_id"`
	Key         string   `json:"key" yaml:"key"`
	Topic       string   `json:"topic" yaml:"topic"`
	MaxMsgBytes int      `json:"max_msg_bytes" yaml:"max_msg_bytes"`
	TimeoutMS   int      `json:"timeout_ms" yaml:"timeout_ms"`
	AckReplicas bool     `json:"ack_replicas" yaml:"ack_replicas"`
}

// NewKafkaConfig creates a new KafkaConfig with default values.
func NewKafkaConfig() KafkaConfig {
	return KafkaConfig{
		Addresses:   []string{"localhost:9092"},
		ClientID:    "benthos_kafka_output",
		Key:         "",
		Topic:       "benthos_stream",
		MaxMsgBytes: 1000000,
		TimeoutMS:   5000,
		AckReplicas: true,
	}
}

//------------------------------------------------------------------------------

// Kafka is a writer type that writes messages into kafka.
type Kafka struct {
	log   log.Modular
	stats metrics.Type

	addresses []string
	conf      KafkaConfig

	keyBytes       []byte
	interpolateKey bool

	producer sarama.SyncProducer
}

// NewKafka creates a new Kafka writer type.
func NewKafka(conf KafkaConfig, log log.Modular, stats metrics.Type) (*Kafka, error) {
	keyBytes := []byte(conf.Key)
	interpolateKey := text.ContainsFunctionVariables(keyBytes)

	k := Kafka{
		log:            log.NewModule(".output.kafka"),
		stats:          stats,
		conf:           conf,
		keyBytes:       keyBytes,
		interpolateKey: interpolateKey,
	}

	for _, addr := range conf.Addresses {
		for _, splitAddr := range strings.Split(addr, ",") {
			if len(splitAddr) > 0 {
				k.addresses = append(k.addresses, splitAddr)
			}
		}
	}

	return &k, nil
}

//------------------------------------------------------------------------------

// Connect attempts to establish a connection to a Kafka broker.
func (k *Kafka) Connect() error {
	if k.producer != nil {
		return nil
	}

	config := sarama.NewConfig()
	config.ClientID = k.conf.ClientID

	config.Producer.MaxMessageBytes = k.conf.MaxMsgBytes
	config.Producer.Timeout = time.Duration(k.conf.TimeoutMS) * time.Millisecond
	config.Producer.Return.Errors = true
	config.Producer.Return.Successes = true

	if k.conf.AckReplicas {
		config.Producer.RequiredAcks = sarama.WaitForAll
	} else {
		config.Producer.RequiredAcks = sarama.WaitForLocal
	}

	var err error
	k.producer, err = sarama.NewSyncProducer(k.addresses, config)

	if err == nil {
		k.log.Infof("Sending Kafka messages to addresses: %s\n", k.addresses)
	}
	return err
}

// Write will attempt to write a message to Kafka, wait for acknowledgement, and
// returns an error if applicable.
func (k *Kafka) Write(msg types.Message) error {
	if k.producer == nil {
		return types.ErrNotConnected
	}

	msgs := []*sarama.ProducerMessage{}
	for _, part := range msg.Parts {
		if len(part) > k.conf.MaxMsgBytes {
			k.stats.Incr("output.kafka.send.dropped.max_msg_bytes", 1)
			continue
		}

		key := k.keyBytes
		if k.interpolateKey {
			key = text.ReplaceFunctionVariables(k.keyBytes)
		}
		nextMsg := &sarama.ProducerMessage{
			Topic: k.conf.Topic,
			Value: sarama.ByteEncoder(part),
		}
		if len(key) > 0 {
			nextMsg.Key = sarama.ByteEncoder(key)
		}
		msgs = append(msgs, nextMsg)
	}

	return k.producer.SendMessages(msgs)
}

// CloseAsync shuts down the Kafka writer and stops processing messages.
func (k *Kafka) CloseAsync() {
}

// WaitForClose blocks until the Kafka writer has closed down.
func (k *Kafka) WaitForClose(timeout time.Duration) error {
	if nil != k.producer {
		k.producer.Close()
		k.producer = nil
	}
	return nil
}

//------------------------------------------------------------------------------
<commit_msg>Improve kafka error message<commit_after>// Copyright (c) 2014 Ashley Jeffs
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package writer

import (
	"fmt"
	"strings"
	"time"

	"github.com/Jeffail/benthos/lib/types"
	"github.com/Jeffail/benthos/lib/util/service/log"
	"github.com/Jeffail/benthos/lib/util/service/metrics"
	"github.com/Jeffail/benthos/lib/util/text"
	"github.com/Shopify/sarama"
)

//------------------------------------------------------------------------------

// KafkaConfig is configuration for the Kafka output type.
type KafkaConfig struct {
	Addresses   []string `json:"addresses" yaml:"addresses"`
	ClientID    string   `json:"client_id" yaml:"client_id"`
	Key         string   `json:"key" yaml:"key"`
	Topic       string   `json:"topic" yaml:"topic"`
	MaxMsgBytes int      `json:"max_msg_bytes" yaml:"max_msg_bytes"`
	TimeoutMS   int      `json:"timeout_ms" yaml:"timeout_ms"`
	AckReplicas bool     `json:"ack_replicas" yaml:"ack_replicas"`
}

// NewKafkaConfig creates a new KafkaConfig with default values.
func NewKafkaConfig() KafkaConfig {
	return KafkaConfig{
		Addresses:   []string{"localhost:9092"},
		ClientID:    "benthos_kafka_output",
		Key:         "",
		Topic:       "benthos_stream",
		MaxMsgBytes: 1000000,
		TimeoutMS:   5000,
		AckReplicas: true,
	}
}

//------------------------------------------------------------------------------

// Kafka is a writer type that writes messages into kafka.
type Kafka struct {
	log   log.Modular
	stats metrics.Type

	addresses []string
	conf      KafkaConfig

	keyBytes       []byte
	interpolateKey bool

	producer sarama.SyncProducer
}

// NewKafka creates a new Kafka writer type.
func NewKafka(conf KafkaConfig, log log.Modular, stats metrics.Type) (*Kafka, error) {
	keyBytes := []byte(conf.Key)
	interpolateKey := text.ContainsFunctionVariables(keyBytes)

	k := Kafka{
		log:            log.NewModule(".output.kafka"),
		stats:          stats,
		conf:           conf,
		keyBytes:       keyBytes,
		interpolateKey: interpolateKey,
	}

	for _, addr := range conf.Addresses {
		for _, splitAddr := range strings.Split(addr, ",") {
			if len(splitAddr) > 0 {
				k.addresses = append(k.addresses, splitAddr)
			}
		}
	}

	return &k, nil
}

//------------------------------------------------------------------------------

// Connect attempts to establish a connection to a Kafka broker.
func (k *Kafka) Connect() error {
	if k.producer != nil {
		return nil
	}

	config := sarama.NewConfig()
	config.ClientID = k.conf.ClientID

	config.Producer.MaxMessageBytes = k.conf.MaxMsgBytes
	config.Producer.Timeout = time.Duration(k.conf.TimeoutMS) * time.Millisecond
	config.Producer.Return.Errors = true
	config.Producer.Return.Successes = true

	if k.conf.AckReplicas {
		config.Producer.RequiredAcks = sarama.WaitForAll
	} else {
		config.Producer.RequiredAcks = sarama.WaitForLocal
	}

	var err error
	k.producer, err = sarama.NewSyncProducer(k.addresses, config)

	if err == nil {
		k.log.Infof("Sending Kafka messages to addresses: %s\n", k.addresses)
	}
	return err
}

// Write will attempt to write a message to Kafka, wait for acknowledgement, and
// returns an error if applicable.
func (k *Kafka) Write(msg types.Message) error {
	if k.producer == nil {
		return types.ErrNotConnected
	}

	msgs := []*sarama.ProducerMessage{}
	for _, part := range msg.Parts {
		if len(part) > k.conf.MaxMsgBytes {
			k.stats.Incr("output.kafka.send.dropped.max_msg_bytes", 1)
			continue
		}

		key := k.keyBytes
		if k.interpolateKey {
			key = text.ReplaceFunctionVariables(k.keyBytes)
		}
		nextMsg := &sarama.ProducerMessage{
			Topic: k.conf.Topic,
			Value: sarama.ByteEncoder(part),
		}
		if len(key) > 0 {
			nextMsg.Key = sarama.ByteEncoder(key)
		}
		msgs = append(msgs, nextMsg)
	}

	err := k.producer.SendMessages(msgs)
	if err != nil {
		if pErr, ok := err.(sarama.ProducerErrors); ok && len(pErr) > 0 {
			err = fmt.Errorf("failed to send %v parts from message: %v\n", len(pErr), pErr[0].Err)
		}
	}

	return err
}

// CloseAsync shuts down the Kafka writer and stops processing messages.
func (k *Kafka) CloseAsync() {
}

// WaitForClose blocks until the Kafka writer has closed down.
func (k *Kafka) WaitForClose(timeout time.Duration) error {
	if nil != k.producer {
		k.producer.Close()
		k.producer = nil
	}
	return nil
}

//------------------------------------------------------------------------------
<|endoftext|>


<commit_before>// Copyright 2014 Markus Dittrich. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
// gobble is a simple program for retrieving files via
// http, https, and ftp á la wget

package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"
)

// command line settings
var (
	urlTarget   = flag.String("u", "", "url to download")
	outFileName = flag.String("o", "", "name of output file")
	toStdout    = flag.Bool("s", false, "output to stdout")
)

// general settings
var (
	numBytes = 40960 // chunk site for reading and writing
	version  = 0.1   // gobble version
)

// progress bar
var progressBar = "-----------------------------------"

func main() {

	flag.Parse()
	if *urlTarget == "" {
		usage()
	}
	url := normalizeURLTarget(*urlTarget)

	// start http client
	client := &http.Client{}
	resp, err := client.Get(url)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()

	// open output file; nil if stdout was requested
	file := os.Stdout
	if !*toStdout {
		file, err = openOutfile(*outFileName, url)
		if err != nil {
			log.Fatal("failed to open output file: ", err)
		}
		defer file.Close()
		printInfo(url, resp)
	}

	totalBytes := resp.ContentLength
	bytesRead, err := copyContent(resp.Body, file, totalBytes, *toStdout)
	if err != nil {
		log.Fatal(err)
	}

	if !*toStdout {
		fmt.Println(statusString(bytesRead, totalBytes))
	}
}

// copyContent reads the body content from the http connection and then
// copies it either to the provided file or stdou
func copyContent(body io.ReadCloser, file *os.File, totalBytes int64,
	wantStdout bool) (int, error) {

	buffer := make([]byte, numBytes)
	bytesRead := 0
	n := 0
	for {
		// read numBytes
		var err error
		n, err = io.ReadFull(body, buffer)
		if err != nil {
			if err == io.EOF || err == io.ErrUnexpectedEOF {
				break // this is the regular end-of-file - we are done
			} else {
				return 0, err
			}
		}

		// write numBytes
		nOut, err := bufWrite(buffer, file)
		if err != nil {
			log.Fatal(err)
		} else if nOut != n {
			return 0, fmt.Errorf("% bytes read but %d byte written", n, nOut)
		}

		bytesRead += n
		if !wantStdout {
			fmt.Print(statusString(bytesRead, totalBytes))
		}
	}

	// write whatever is left
	_, err := bufWrite(buffer[:n], file)
	if err != nil {
		return 0, err
	}

	bytesRead += n
	return bytesRead, nil
}

// bufWrite writes content either to stdout or the requested output file
func bufWrite(content []byte, file *os.File) (int, error) {
	n, err := file.Write(content)
	if err != nil {
		return n, err
	}
	return n, nil
}

// openOutfile opens the output file if one was requested
// Otherwise, we assume the output file is index.html
func openOutfile(outFileName, urlTarget string) (*os.File, error) {

	fileName := outFileName
	if fileName == "" {

		// can we extract a
		urlInfo, err := url.Parse(urlTarget)
		if err != nil {
			return nil, err
		}
		if fileName = filepath.Base(urlInfo.Path); fileName == "." || fileName == "/" {
			fileName = "index.html"
		}
	}

	// if fileName already exists we bail
	if _, err := os.Stat(fileName); err == nil {
		return nil, fmt.Errorf("%s already exists\n", fileName)
	}

	file, err := os.Create(fileName)
	if err != nil {
		return nil, err
	}

	return file, nil
}

// normalizeURLTarget currently only checks if an URL starts with
// http:// and if not appends it
func normalizeURLTarget(urlTarget string) string {
	outString := urlTarget
	if !strings.HasPrefix(urlTarget, "http://") {
		outString = "http://" + urlTarget
	}
	return outString
}

// statusString returns the status string corresponding to the given
// number of bytes read.
// NOTE: Sites which don't provide the content length return a value of
// -1 for totalbytes. In this case we print a simpler content string
func statusString(bytesRead int, totalBytes int64) string {
	var formatString string
	if totalBytes == -1 {
		progressString := "<=>"
		formatString = fmt.Sprintf("progress: %10d Bytes    %-30s  \r", bytesRead,
			progressString)
	} else {
		percentage := float64(bytesRead) / float64(totalBytes) * 100
		progressString := strings.Join(
			[]string{progressBar[1 : 2+int(percentage/4)], ">"}, "")
		formatString = fmt.Sprintf("progress: %10d Bytes    %-30s  %2.1f%%\r", bytesRead,
			progressString, percentage)
	}
	return formatString
}

// printInfo prints a brief informative header about the connection
func printInfo(urlTarget string, resp *http.Response) {
	fmt.Println("********* This is gobble version ", version, " ***************")

	urlInfo, err := url.Parse(urlTarget)
	if err != nil {
		return
	}
	cname, _ := net.LookupCNAME(urlInfo.Host)
	ips, _ := net.LookupIP(cname)
	fmt.Println("Connecting to", cname, "  ", ips)
	fmt.Printf("Status %s   Protocol %s  TransferEncoding %v\n", resp.Status,
		resp.Proto, resp.TransferEncoding)
	fmt.Printf("Content Length: %d bytes\n", resp.ContentLength)
	fmt.Println()
}

// usage prints the package usage and then exits
func usage() {
	fmt.Println(os.Args[0], "[options]", "\n\noptions:")
	flag.PrintDefaults()
	os.Exit(1)
}
<commit_msg>Improved output messages.<commit_after>// Copyright 2014 Markus Dittrich. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
// gobble is a simple program for retrieving files via
// http, https, and ftp á la wget

package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"
)

// command line settings
var (
	urlTarget   = flag.String("u", "", "url to download")
	outFileName = flag.String("o", "", "name of output file")
	toStdout    = flag.Bool("s", false, "output to stdout")
)

// general settings
var (
	numBytes = 40960 // chunk site for reading and writing
	version  = 0.1   // gobble version
)

// progress bar
var progressBar = "-----------------------------------"

func main() {

	flag.Parse()
	if *urlTarget == "" {
		usage()
	}
	url := normalizeURLTarget(*urlTarget)

	// start http client
	client := &http.Client{}
	resp, err := client.Get(url)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()

	// open output file; nil if stdout was requested
	file := os.Stdout
	if !*toStdout {
		file, err = openOutfile(*outFileName, url)
		if err != nil {
			log.Fatal("failed to open output file: ", err)
		}
		defer file.Close()
		printInfo(url, resp)
	}

	totalBytes := resp.ContentLength
	bytesRead, err := copyContent(resp.Body, file, totalBytes, *toStdout)
	if err != nil {
		log.Fatal(err)
	}

	if !*toStdout {
		fmt.Println(statusString(bytesRead, totalBytes, true))
	}
}

// copyContent reads the body content from the http connection and then
// copies it either to the provided file or stdou
func copyContent(body io.ReadCloser, file *os.File, totalBytes int64,
	wantStdout bool) (int, error) {

	buffer := make([]byte, numBytes)
	bytesRead := 0
	n := 0
	for {
		// read numBytes
		var err error
		n, err = io.ReadFull(body, buffer)
		if err != nil {
			if err == io.EOF || err == io.ErrUnexpectedEOF {
				break // this is the regular end-of-file - we are done
			} else {
				return 0, err
			}
		}

		// write numBytes
		nOut, err := bufWrite(buffer, file)
		if err != nil {
			log.Fatal(err)
		} else if nOut != n {
			return 0, fmt.Errorf("% bytes read but %d byte written", n, nOut)
		}

		bytesRead += n
		if !wantStdout {
			fmt.Print(statusString(bytesRead, totalBytes, false))
		}
	}

	// write whatever is left
	_, err := bufWrite(buffer[:n], file)
	if err != nil {
		return 0, err
	}

	bytesRead += n
	return bytesRead, nil
}

// bufWrite writes content either to stdout or the requested output file
func bufWrite(content []byte, file *os.File) (int, error) {
	n, err := file.Write(content)
	if err != nil {
		return n, err
	}
	return n, nil
}

// openOutfile opens the output file if one was requested
// Otherwise, we assume the output file is index.html
func openOutfile(outFileName, urlTarget string) (*os.File, error) {

	fileName := outFileName
	if fileName == "" {

		// can we extract a
		urlInfo, err := url.Parse(urlTarget)
		if err != nil {
			return nil, err
		}
		if fileName = filepath.Base(urlInfo.Path); fileName == "." || fileName == "/" {
			fileName = "index.html"
		}
	}

	// if fileName already exists we bail
	if _, err := os.Stat(fileName); err == nil {
		return nil, fmt.Errorf("%s already exists\n", fileName)
	}

	file, err := os.Create(fileName)
	if err != nil {
		return nil, err
	}

	return file, nil
}

// normalizeURLTarget currently only checks if an URL starts with
// http:// and if not appends it
func normalizeURLTarget(urlTarget string) string {
	outString := urlTarget
	if !strings.HasPrefix(urlTarget, "http://") {
		outString = "http://" + urlTarget
	}
	return outString
}

// statusString returns the status string corresponding to the given
// number of bytes read.
// NOTE: Sites which don't provide the content length return a value of
// -1 for totalbytes. In this case we print a simpler content string
func statusString(bytesRead int, totalBytes int64, allDone bool) string {
	var msg string
	if allDone {
		msg = "Finished:    "
	} else {
		msg = "In progress: "
	}
	var formatString string
	if totalBytes == -1 {
		progressString := "<=>"
		formatString = fmt.Sprintf("%s %10d Bytes    %-30s  \r", msg, bytesRead,
			progressString)
	} else {
		percentage := float64(bytesRead) / float64(totalBytes) * 100
		progressString := strings.Join(
			[]string{progressBar[1 : 2+int(percentage/4)], ">"}, "")
		formatString = fmt.Sprintf("%s %10d Bytes    %-30s  %2.1f%%\r", msg,
			bytesRead, progressString, percentage)
	}
	return formatString
}

// printInfo prints a brief informative header about the connection
func printInfo(urlTarget string, resp *http.Response) {
	fmt.Println("********* This is gobble version ", version, " ***************")

	urlInfo, err := url.Parse(urlTarget)
	if err != nil {
		return
	}
	cname, _ := net.LookupCNAME(urlInfo.Host)
	ips, _ := net.LookupIP(cname)
	fmt.Println("Connecting to", cname, "  ", ips)
	fmt.Printf("Status %s   Protocol %s  TransferEncoding %v\n", resp.Status,
		resp.Proto, resp.TransferEncoding)
	fmt.Printf("Content Length: %d bytes\n", resp.ContentLength)
	fmt.Println()
}

// usage prints the package usage and then exits
func usage() {
	fmt.Println(os.Args[0], "[options]", "\n\noptions:")
	flag.PrintDefaults()
	os.Exit(1)
}
<|endoftext|>


<commit_before>package WatchDog

import (
	"math"
	"sync/atomic"
	"time"

	"github.com/6xiao/go/Common"
)

type WatchDog struct {
	wait time.Duration
	hung func()
	meat int32
}

func NewDog(duration time.Duration, meat int32, hung func()) *WatchDog {
	d := new(WatchDog)
	d.wait = duration
	d.hung = hung
	d.meat = meat

	go d.eat()
	return d
}

func (this *WatchDog) eat() {
	defer Common.CheckPanic()

	for this.hung != nil {
		time.Sleep(this.wait)

		m := atomic.LoadInt32(&this.meat)
		if m < 0 {
			return
		}

		if m == 0 {
			this.hung()
		} else {
			atomic.StoreInt32(&this.meat, m/2)
		}
	}
}

func (this *WatchDog) Feed(meat uint16) bool {
	defer Common.CheckPanic()
	return atomic.AddInt32(&this.meat, int32(meat)) > 0
}

func (this *WatchDog) Kill() {
	defer Common.CheckPanic()
	atomic.StoreInt32(&this.meat, -65536)
}
<commit_msg>change 64bit to 32bit<commit_after>package WatchDog

import (
	"sync/atomic"
	"time"

	"github.com/6xiao/go/Common"
)

type WatchDog struct {
	wait time.Duration
	hung func()
	meat int32
}

func NewDog(duration time.Duration, meat int32, hung func()) *WatchDog {
	d := new(WatchDog)
	d.wait = duration
	d.hung = hung
	d.meat = meat

	go d.eat()
	return d
}

func (this *WatchDog) eat() {
	defer Common.CheckPanic()

	for this.hung != nil {
		time.Sleep(this.wait)

		m := atomic.LoadInt32(&this.meat)
		if m < 0 {
			return
		}

		if m == 0 {
			this.hung()
		} else {
			atomic.StoreInt32(&this.meat, m/2)
		}
	}
}

func (this *WatchDog) Feed(meat uint16) bool {
	defer Common.CheckPanic()
	return atomic.AddInt32(&this.meat, int32(meat)) > 0
}

func (this *WatchDog) Kill() {
	defer Common.CheckPanic()
	atomic.StoreInt32(&this.meat, -65536)
}
<|endoftext|>


<commit_before>package gocqrs

import (
	"errors"
	"fmt"
)

var (
	// ErrInvalidApplication is used to inform a consumer when they've
	// provided an aggregate that doesn't have a valid application id
	// that the receiving service is able to process
	ErrInvalidApplication = errors.New("invalid application identifier")

	// ErrInvalidDomain is used to inform a consumer when they've
	// provided an aggregate that doesn't have a valid domain id that
	// the receiving service is able to process
	// * Domain is semantically equal to Aggregate Type
	ErrInvalidDomain = errors.New("invalid domain identifier")

	// ErrInvalidAggregateId is used to inform a consumer when they've
	// provided an aggregate id that is not available due to either
	// overlap with an existing aggregate or domain specific command
	// handler rules
	ErrInvalidAggregateId = errors.New("invalid aggregate identifier")

	// ErrInvalidVersion is used to inform a consumer when they've
	// provided an aggregate with a version that cannot be sync'd
	// with the current domain version
	ErrInvalidVersion = errors.New("invalid aggregate version")

	// ErrInvalidCommandType is used to inform a consumer when they've
	// provided a command type that isn't valid for the application and
	// domain partition
	ErrInvalidCommandType = errors.New("invalid command type identifier")

	// ErrInvalidEventType is used to inform a consumer when they've
	// provided an event type that isn't valid for the application and
	// domain partition
	ErrInvalidEventType = errors.New("invalid event type identifier")

	// ErrUnableToFindAggregate is used to inform a consumer when the
	// aggregate associate with a command wasn't found in the store
	ErrUnableToFindAggregate = errors.New("unable to locate specified aggregate")

	// ErrUnableToLoadAggregate is used to inform a consumer when the
	// aggregate loaded from the store failed to hydrate properly
	ErrUnableToLoadAggregate = errors.New("error occured loading aggregate")

	// ErrErrorApplyingCommand is used to inform a consumer when the
	// command handler returns an errory when applying the command
	// to the target aggregate
	ErrErrorApplyingCommand = errors.New("error occured applying command")

	// ErrErrorAppendingEvent is used to inform a consumer when there
	// is an error appending the event to the eventstore
	ErrErrorAppendingEvent = errors.New("error writing to the eventstore")

	// ErrErrorPublishingEvent is used to inform a consumer when there
	// is an error publishing the event produced by the command handler
	// This step occurs after the event has been stored
	ErrErrorPublishingEvent = errors.New("error publishing the event")
)

// NoOrigin is the default value to use for origin commands which were not
// a result of a previous event.  Used to specify no causation or initial action.
const NoOrigin = NewAggregate(0, 0, 0, 0)

// TypeBuilder describes a function that can be used to produce a type id
type TypeBuilder func(uint8, uint32) uint32

// MakeVersionedCommandType provides a utility to union a command's version and
// type identifiers and masks off the leftmost bit as 1 to indicate a command
func MakeVersionedCommandType(version uint8, typeId uint32) uint32 {
	return 0x80000000 | (uint32(version) << 24 & 0x7F000000) | (typeId & 0xFFFFFF)
}

// MakeVersionedEventType provides a utility to union an event's version and
// type identifiers and masks off the leftmost bit as 0 to indicate an event
func MakeVersionedEventType(version uint8, typeId uint32) uint32 {
	return 0x7FFFFFFF&(uint32(version)<<24&0x7F000000) | (typeId & 0xFFFFFF)
}

// EventStoreReaderWriterGenerator describes a type the can be used to either
// read or write events to an eventstore or generate a safe uuid
type EventStoreReaderWriterGenerator interface {
	AggregateIdGenerator
	EventStoreWriter
	EventStoreReader
}

// AggregateIdGenerator is responsible for creating valid unique Ids for Aggregates
type AggregateIdGenerator interface {
	//GenerateAggregateId(application uint32, domain uint32) (uint64, error)
	GenerateAggregateId() (uint64, error)
}

// EventWriter is responsible for persisting Events to the EventStore
type EventStoreWriter interface {
	AppendEvent(Event) (int64, error)
}

// EventStoreReader is responsible for serving Streams as queries against the EventStore
type EventStoreReader interface {
	LoadEvents() ([]Event, error)
	LoadEventsByAggregate(aggregate uint64) ([]Event, error)
	LoadEventsByEventType(eventType uint32) ([]Event, error)
	LoadEventsByEventTypes(eventTypes ...uint32) ([]Event, error)
	LoadEventsFromTimestamp(timestamp int64) (int64, []Event, error)
	LoadEventsByAggregateFromTimestamp(timestamp int64, aggregate uint64) (int64, []Event, error)
	LoadEventsByEventTypeFromTimestamp(timestamp int64, eventType uint32) (int64, []Event, error)
	LoadEventsByEventTypesFromTimestamp(timestamp int64, eventTypes ...uint32) (int64, []Event, error)
}

// Aggregate provides a base interface for things that contain
// aggregate header information
type Aggregate interface {
	GetApplication() uint32
	GetDomain() uint32
	GetId() uint64
	GetVersion() uint32
	String() string
}

// AggregateHydrator describes a type which processes a slice of events to produce
// a populated aggregate instance
type AggregateHydrator interface {
	LoadAggregate([]Event) (Aggregate, error)
}

// Command provides a base interface for all commands in the
// system which includes aggregate header information to identity
// the target of the command
type Command interface {
	Aggregate
	GetCommandType() uint32
	GetOrigin() Aggregate
}

// CommandHandler describes a type that can be used to process commands
type CommandHandler interface {
	Handle(command Command) error
}

// CommandSerializerDeSerializer  describes a type that can be used to
// either serialize or deserialize a Command to/from a byte slice
type CommandSerializerDeserializer interface {
	CommandSerializer
	CommandDeserializer
}

// CommandSerializer describes a type that can be used to serialize
// Commands to a raw byte slice
type CommandSerializer interface {
	Serialize(Command) ([]byte, error)
}

// CommandDeserializer describes a type that can be used to deserialize
// Commands from a raw byte slice
type CommandDeserializer interface {
	Deserialize([]byte) (Command, error)
}

// TypedCommandSerializerDeserializer describes a type that can be used to serialize
// or deserialize Cp,,amds from a raw byte slice given the commandType
type TypedCommandSerializerDeserializer interface {
	CommandSerializer
	TypedCommandDeserializer
}

// TypedCommandDeserializer describes a type that can be used to deserialize
// Command from a raw byte slice given the commandType
type TypedCommandDeserializer interface {
	Deserialize(uint32, []byte) (Command, error)
}

// Event provides a base interface for all events in the system
// which includes aggregate header information to identify the
// target of the event
type Event interface {
	Aggregate
	GetEventType() uint32
	GetOrigin() Aggregate
}

// EventPublisher describes a type that can be used to publish events to a bus
type EventPublisher interface {
	Publish(int64, Event) error
}

// EventHandler describes a type that can be used to process events
type EventHandler interface {
	Handle(event Event) (int64, error)
}

// EventSerializerDeSerializer  describes a type that can be used to
// either serialize or deserialize an Event to/from a byte slice
type EventSerializerDeserializer interface {
	EventSerializer
	EventDeserializer
}

// EventSerializer describes a type that can be used to serialize
// Events to a raw byte slice
type EventSerializer interface {
	Serialize(Event) ([]byte, error)
}

// EventDeserializer describes a type that can be used to deserialize
// Events from a raw byte slice
type EventDeserializer interface {
	Deserialize([]byte) (Event, error)
}

// TypedEventSerializerDeserializer describes a type that can be used to serialize
// or deserialize Events from a raw byte slice given the eventType
type TypedEventSerializerDeserializer interface {
	EventSerializer
	TypedEventDeserializer
}

// TypedEventDeserializer describes a type that can be used to deserialize
// Events from a raw byte slice given the eventType
type TypedEventDeserializer interface {
	Deserialize(uint32, []byte) (Event, error)
}

// AggregateMemento is a structured header describing the UUId of an aggregate instance
type AggregateMemento struct {
	// application the target aggregate belongs to, provides multi-tenancy
	// at the application level partition for like domains within the same service
	Application uint32 `json:"_app"`
	// domain is the type of aggregate (type is semantically equivalent to doman)
	Domain uint32 `json:"_domain"`
	// id is an [application / domain] unique identifier for the aggregate instance
	// and should never be duplicated within that partition
	Id uint64 `json:"_id"`
	// version is derived from the number of events applied to the aggregate
	// and provides guaranteed event ordering within it's
	// [appliction / domain / id] partition
	Version uint32 `json:"_ver"`
}

// NewAggregate creates an aggregate instance with UUId derived from the provided values
func NewAggregate(application uint32, domain uint32, id uint64, version uint32) AggregateMemento {
	return AggregateMemento{
		Application: application,
		Domain:      domain,
		Id:          id,
		Version:     version,
	}
}

// GetApplication returns the application id this aggregate
// was designed within
func (aggregate AggregateMemento) GetApplication() uint32 {
	return aggregate.Application
}

// GetDomain returns the domain (or aggregate type) of this aggregate
func (aggregate AggregateMemento) GetDomain() uint32 {
	return aggregate.Domain
}

// GetId returns the id of the aggregate which is unique within the
// partition provided by the combination of application and domain
func (aggregate AggregateMemento) GetId() uint64 {
	return aggregate.Id
}

// GetVersion returns the version of the aggregate represented by
// this aggregate instance.  Not guaranteed to be the current version
// just the version state of the aggregate when this instance was
// loaded
func (aggregate AggregateMemento) GetVersion() uint32 {
	return aggregate.Version
}

// String returns the string representation of the aggregate
func (aggregate AggregateMemento) String() string {
	return fmt.Sprintf("%d%d%d%d", aggregate.Application, aggregate.Domain, aggregate.Id, aggregate.Version)
}

// CommandMemento is a structured header describing the UUID of a Command instance
type CommandMemento struct {
	// aggregate is the base structure that binds the command instance
	// to the target aggregate by capturing the aggregate's full UUId
	// partition information [ application / domain / id / version ]
	AggregateMemento
	// origin is the correlary structure that links a command to its legacy
	Origin AggregateMemento
	// commandType is an [ application / domain ] unique identifier for the type of
	// command message which captures the semantic intent of the command
	CommandType uint32 `json:"_ctype"`
}

// NewCommand creates a command instance with UUID derived from the provided values
// including the header of the targeted aggregate instance
func NewCommand(application uint32, domain uint32, id uint64, version uint32, commandType uint32, origin Aggregate) CommandMemento {
	return CommandMemento{
		AggregateMemento: AggregateMemento{
			Application: application,
			Domain:      domain,
			Id:          id,
			Version:     version,
		},
		Origin: AggregateMemento{
			Application: origin.GetApplication(),
			Domain:      origin.GetDomain(),
			Id:          origin.GetId(),
			Version:     origin.GetVersion(),
		},
		CommandType: commandType,
	}
}

// GetCommandType returns the command type of the event that is unique within
// the [ application / domain ] partition
func (command CommandMemento) GetCommandType() uint32 {
	return command.CommandType
}

func (command CommandMemento) GetOrigin() Aggregate {
	return command.Origin
}

// EventMemento is a structured header describing the UUID of an Event instance
type EventMemento struct {
	// aggregate is the base structure that binds the event instance
	// to the target aggregate by capturing the aggregate's full UUId
	// partition information [ application / domain / id / version ]
	AggregateMemento
	// origin is the correlary structure that links a command to its legacy
	Origin AggregateMemento
	// eventType is an [ application / domain ] unique identifier for the type of
	// event message which captures the semantic intent of the event
	EventType uint32 `json:"_etype"`
}

// NewEvent creates an event instance with UUID derived from the provided values
// including the header of the targeted aggregate instance
func NewEvent(application uint32, domain uint32, id uint64, version uint32, eventType uint32, origin Aggregate) EventMemento {
	return EventMemento{
		AggregateMemento: AggregateMemento{
			Application: application,
			Domain:      domain,
			Id:          id,
			Version:     version,
		},
		Origin: AggregateMemento{
			Application: origin.GetApplication(),
			Domain:      origin.GetDomain(),
			Id:          origin.GetId(),
			Version:     origin.GetVersion(),
		},
		EventType: eventType,
	}
}

// GetEventType returns the event type of the event that is unique within
// the [ application / domain ] partition
func (event EventMemento) GetEventType() uint32 {
	return event.EventType
}

func (event EventMemento) GetOrigin() Aggregate {
	return event.Origin
}

// AggregateLoader describes a function which takes a slice of events and
// produces either a valid aggregate or an error
type AggregateLoader func([]Event) (Aggregate, error)

// CommandEvaluator describes a function which evaluates a
type CommandEvaluator func(AggregateIdGenerator, Aggregate, Command) (Event, error)

// DefaultCommandHandler provides a base implementation for domain specific command
// handlers to use if they follow a standard execution path
func DefaultCommandHandler(eventStore EventStoreReaderWriterGenerator, publisher EventPublisher, loader AggregateLoader, evaluator CommandEvaluator, command Command) (err error) {
	// Read the events from the store
	events, err := eventStore.LoadEventsByAggregate(command.GetId())
	if err != nil {
		return ErrUnableToFindAggregate
	}
	// Populate an aggregate using the retrieved events
	aggregate, err := loader(events)
	if err != nil {
		return ErrUnableToLoadAggregate
	}
	// Evaluate the command against the aggregate
	event, err := evaluator(eventStore, aggregate, command)
	if err != nil {
		return ErrErrorApplyingCommand
	}
	// Commit the event to the eventstore
	timestamp, err := eventStore.AppendEvent(event)
	if err != nil {
		return ErrErrorAppendingEvent
	}
	// Broadcast the created event to all observers
	err = publisher.Publish(timestamp, event)
	if err != nil {
		return ErrErrorPublishingEvent
	}
	return err
}
<commit_msg>cannot const aggregate<commit_after>package gocqrs

import (
	"errors"
	"fmt"
)

var (
	// ErrInvalidApplication is used to inform a consumer when they've
	// provided an aggregate that doesn't have a valid application id
	// that the receiving service is able to process
	ErrInvalidApplication = errors.New("invalid application identifier")

	// ErrInvalidDomain is used to inform a consumer when they've
	// provided an aggregate that doesn't have a valid domain id that
	// the receiving service is able to process
	// * Domain is semantically equal to Aggregate Type
	ErrInvalidDomain = errors.New("invalid domain identifier")

	// ErrInvalidAggregateId is used to inform a consumer when they've
	// provided an aggregate id that is not available due to either
	// overlap with an existing aggregate or domain specific command
	// handler rules
	ErrInvalidAggregateId = errors.New("invalid aggregate identifier")

	// ErrInvalidVersion is used to inform a consumer when they've
	// provided an aggregate with a version that cannot be sync'd
	// with the current domain version
	ErrInvalidVersion = errors.New("invalid aggregate version")

	// ErrInvalidCommandType is used to inform a consumer when they've
	// provided a command type that isn't valid for the application and
	// domain partition
	ErrInvalidCommandType = errors.New("invalid command type identifier")

	// ErrInvalidEventType is used to inform a consumer when they've
	// provided an event type that isn't valid for the application and
	// domain partition
	ErrInvalidEventType = errors.New("invalid event type identifier")

	// ErrUnableToFindAggregate is used to inform a consumer when the
	// aggregate associate with a command wasn't found in the store
	ErrUnableToFindAggregate = errors.New("unable to locate specified aggregate")

	// ErrUnableToLoadAggregate is used to inform a consumer when the
	// aggregate loaded from the store failed to hydrate properly
	ErrUnableToLoadAggregate = errors.New("error occured loading aggregate")

	// ErrErrorApplyingCommand is used to inform a consumer when the
	// command handler returns an errory when applying the command
	// to the target aggregate
	ErrErrorApplyingCommand = errors.New("error occured applying command")

	// ErrErrorAppendingEvent is used to inform a consumer when there
	// is an error appending the event to the eventstore
	ErrErrorAppendingEvent = errors.New("error writing to the eventstore")

	// ErrErrorPublishingEvent is used to inform a consumer when there
	// is an error publishing the event produced by the command handler
	// This step occurs after the event has been stored
	ErrErrorPublishingEvent = errors.New("error publishing the event")
)

// NoOrigin is the default value to use for origin commands which were not
// a result of a previous event.  Used to specify no causation or initial action.
var NoOrigin = NewAggregate(0, 0, 0, 0)

// TypeBuilder describes a function that can be used to produce a type id
type TypeBuilder func(uint8, uint32) uint32

// MakeVersionedCommandType provides a utility to union a command's version and
// type identifiers and masks off the leftmost bit as 1 to indicate a command
func MakeVersionedCommandType(version uint8, typeId uint32) uint32 {
	return 0x80000000 | (uint32(version) << 24 & 0x7F000000) | (typeId & 0xFFFFFF)
}

// MakeVersionedEventType provides a utility to union an event's version and
// type identifiers and masks off the leftmost bit as 0 to indicate an event
func MakeVersionedEventType(version uint8, typeId uint32) uint32 {
	return 0x7FFFFFFF&(uint32(version)<<24&0x7F000000) | (typeId & 0xFFFFFF)
}

// EventStoreReaderWriterGenerator describes a type the can be used to either
// read or write events to an eventstore or generate a safe uuid
type EventStoreReaderWriterGenerator interface {
	AggregateIdGenerator
	EventStoreWriter
	EventStoreReader
}

// AggregateIdGenerator is responsible for creating valid unique Ids for Aggregates
type AggregateIdGenerator interface {
	//GenerateAggregateId(application uint32, domain uint32) (uint64, error)
	GenerateAggregateId() (uint64, error)
}

// EventWriter is responsible for persisting Events to the EventStore
type EventStoreWriter interface {
	AppendEvent(Event) (int64, error)
}

// EventStoreReader is responsible for serving Streams as queries against the EventStore
type EventStoreReader interface {
	LoadEvents() ([]Event, error)
	LoadEventsByAggregate(aggregate uint64) ([]Event, error)
	LoadEventsByEventType(eventType uint32) ([]Event, error)
	LoadEventsByEventTypes(eventTypes ...uint32) ([]Event, error)
	LoadEventsFromTimestamp(timestamp int64) (int64, []Event, error)
	LoadEventsByAggregateFromTimestamp(timestamp int64, aggregate uint64) (int64, []Event, error)
	LoadEventsByEventTypeFromTimestamp(timestamp int64, eventType uint32) (int64, []Event, error)
	LoadEventsByEventTypesFromTimestamp(timestamp int64, eventTypes ...uint32) (int64, []Event, error)
}

// Aggregate provides a base interface for things that contain
// aggregate header information
type Aggregate interface {
	GetApplication() uint32
	GetDomain() uint32
	GetId() uint64
	GetVersion() uint32
	String() string
}

// AggregateHydrator describes a type which processes a slice of events to produce
// a populated aggregate instance
type AggregateHydrator interface {
	LoadAggregate([]Event) (Aggregate, error)
}

// Command provides a base interface for all commands in the
// system which includes aggregate header information to identity
// the target of the command
type Command interface {
	Aggregate
	GetCommandType() uint32
	GetOrigin() Aggregate
}

// CommandHandler describes a type that can be used to process commands
type CommandHandler interface {
	Handle(command Command) error
}

// CommandSerializerDeSerializer  describes a type that can be used to
// either serialize or deserialize a Command to/from a byte slice
type CommandSerializerDeserializer interface {
	CommandSerializer
	CommandDeserializer
}

// CommandSerializer describes a type that can be used to serialize
// Commands to a raw byte slice
type CommandSerializer interface {
	Serialize(Command) ([]byte, error)
}

// CommandDeserializer describes a type that can be used to deserialize
// Commands from a raw byte slice
type CommandDeserializer interface {
	Deserialize([]byte) (Command, error)
}

// TypedCommandSerializerDeserializer describes a type that can be used to serialize
// or deserialize Cp,,amds from a raw byte slice given the commandType
type TypedCommandSerializerDeserializer interface {
	CommandSerializer
	TypedCommandDeserializer
}

// TypedCommandDeserializer describes a type that can be used to deserialize
// Command from a raw byte slice given the commandType
type TypedCommandDeserializer interface {
	Deserialize(uint32, []byte) (Command, error)
}

// Event provides a base interface for all events in the system
// which includes aggregate header information to identify the
// target of the event
type Event interface {
	Aggregate
	GetEventType() uint32
	GetOrigin() Aggregate
}

// EventPublisher describes a type that can be used to publish events to a bus
type EventPublisher interface {
	Publish(int64, Event) error
}

// EventHandler describes a type that can be used to process events
type EventHandler interface {
	Handle(event Event) (int64, error)
}

// EventSerializerDeSerializer  describes a type that can be used to
// either serialize or deserialize an Event to/from a byte slice
type EventSerializerDeserializer interface {
	EventSerializer
	EventDeserializer
}

// EventSerializer describes a type that can be used to serialize
// Events to a raw byte slice
type EventSerializer interface {
	Serialize(Event) ([]byte, error)
}

// EventDeserializer describes a type that can be used to deserialize
// Events from a raw byte slice
type EventDeserializer interface {
	Deserialize([]byte) (Event, error)
}

// TypedEventSerializerDeserializer describes a type that can be used to serialize
// or deserialize Events from a raw byte slice given the eventType
type TypedEventSerializerDeserializer interface {
	EventSerializer
	TypedEventDeserializer
}

// TypedEventDeserializer describes a type that can be used to deserialize
// Events from a raw byte slice given the eventType
type TypedEventDeserializer interface {
	Deserialize(uint32, []byte) (Event, error)
}

// AggregateMemento is a structured header describing the UUId of an aggregate instance
type AggregateMemento struct {
	// application the target aggregate belongs to, provides multi-tenancy
	// at the application level partition for like domains within the same service
	Application uint32 `json:"_app"`
	// domain is the type of aggregate (type is semantically equivalent to doman)
	Domain uint32 `json:"_domain"`
	// id is an [application / domain] unique identifier for the aggregate instance
	// and should never be duplicated within that partition
	Id uint64 `json:"_id"`
	// version is derived from the number of events applied to the aggregate
	// and provides guaranteed event ordering within it's
	// [appliction / domain / id] partition
	Version uint32 `json:"_ver"`
}

// NewAggregate creates an aggregate instance with UUId derived from the provided values
func NewAggregate(application uint32, domain uint32, id uint64, version uint32) AggregateMemento {
	return AggregateMemento{
		Application: application,
		Domain:      domain,
		Id:          id,
		Version:     version,
	}
}

// GetApplication returns the application id this aggregate
// was designed within
func (aggregate AggregateMemento) GetApplication() uint32 {
	return aggregate.Application
}

// GetDomain returns the domain (or aggregate type) of this aggregate
func (aggregate AggregateMemento) GetDomain() uint32 {
	return aggregate.Domain
}

// GetId returns the id of the aggregate which is unique within the
// partition provided by the combination of application and domain
func (aggregate AggregateMemento) GetId() uint64 {
	return aggregate.Id
}

// GetVersion returns the version of the aggregate represented by
// this aggregate instance.  Not guaranteed to be the current version
// just the version state of the aggregate when this instance was
// loaded
func (aggregate AggregateMemento) GetVersion() uint32 {
	return aggregate.Version
}

// String returns the string representation of the aggregate
func (aggregate AggregateMemento) String() string {
	return fmt.Sprintf("%d%d%d%d", aggregate.Application, aggregate.Domain, aggregate.Id, aggregate.Version)
}

// CommandMemento is a structured header describing the UUID of a Command instance
type CommandMemento struct {
	// aggregate is the base structure that binds the command instance
	// to the target aggregate by capturing the aggregate's full UUId
	// partition information [ application / domain / id / version ]
	AggregateMemento
	// origin is the correlary structure that links a command to its legacy
	Origin AggregateMemento
	// commandType is an [ application / domain ] unique identifier for the type of
	// command message which captures the semantic intent of the command
	CommandType uint32 `json:"_ctype"`
}

// NewCommand creates a command instance with UUID derived from the provided values
// including the header of the targeted aggregate instance
func NewCommand(application uint32, domain uint32, id uint64, version uint32, commandType uint32, origin Aggregate) CommandMemento {
	return CommandMemento{
		AggregateMemento: AggregateMemento{
			Application: application,
			Domain:      domain,
			Id:          id,
			Version:     version,
		},
		Origin: AggregateMemento{
			Application: origin.GetApplication(),
			Domain:      origin.GetDomain(),
			Id:          origin.GetId(),
			Version:     origin.GetVersion(),
		},
		CommandType: commandType,
	}
}

// GetCommandType returns the command type of the event that is unique within
// the [ application / domain ] partition
func (command CommandMemento) GetCommandType() uint32 {
	return command.CommandType
}

func (command CommandMemento) GetOrigin() Aggregate {
	return command.Origin
}

// EventMemento is a structured header describing the UUID of an Event instance
type EventMemento struct {
	// aggregate is the base structure that binds the event instance
	// to the target aggregate by capturing the aggregate's full UUId
	// partition information [ application / domain / id / version ]
	AggregateMemento
	// origin is the correlary structure that links a command to its legacy
	Origin AggregateMemento
	// eventType is an [ application / domain ] unique identifier for the type of
	// event message which captures the semantic intent of the event
	EventType uint32 `json:"_etype"`
}

// NewEvent creates an event instance with UUID derived from the provided values
// including the header of the targeted aggregate instance
func NewEvent(application uint32, domain uint32, id uint64, version uint32, eventType uint32, origin Aggregate) EventMemento {
	return EventMemento{
		AggregateMemento: AggregateMemento{
			Application: application,
			Domain:      domain,
			Id:          id,
			Version:     version,
		},
		Origin: AggregateMemento{
			Application: origin.GetApplication(),
			Domain:      origin.GetDomain(),
			Id:          origin.GetId(),
			Version:     origin.GetVersion(),
		},
		EventType: eventType,
	}
}

// GetEventType returns the event type of the event that is unique within
// the [ application / domain ] partition
func (event EventMemento) GetEventType() uint32 {
	return event.EventType
}

func (event EventMemento) GetOrigin() Aggregate {
	return event.Origin
}

// AggregateLoader describes a function which takes a slice of events and
// produces either a valid aggregate or an error
type AggregateLoader func([]Event) (Aggregate, error)

// CommandEvaluator describes a function which evaluates a
type CommandEvaluator func(AggregateIdGenerator, Aggregate, Command) (Event, error)

// DefaultCommandHandler provides a base implementation for domain specific command
// handlers to use if they follow a standard execution path
func DefaultCommandHandler(eventStore EventStoreReaderWriterGenerator, publisher EventPublisher, loader AggregateLoader, evaluator CommandEvaluator, command Command) (err error) {
	// Read the events from the store
	events, err := eventStore.LoadEventsByAggregate(command.GetId())
	if err != nil {
		return ErrUnableToFindAggregate
	}
	// Populate an aggregate using the retrieved events
	aggregate, err := loader(events)
	if err != nil {
		return ErrUnableToLoadAggregate
	}
	// Evaluate the command against the aggregate
	event, err := evaluator(eventStore, aggregate, command)
	if err != nil {
		return ErrErrorApplyingCommand
	}
	// Commit the event to the eventstore
	timestamp, err := eventStore.AppendEvent(event)
	if err != nil {
		return ErrErrorAppendingEvent
	}
	// Broadcast the created event to all observers
	err = publisher.Publish(timestamp, event)
	if err != nil {
		return ErrErrorPublishingEvent
	}
	return err
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"flag"
	"fmt"
	"hash"
	"io"
	"os"
	"runtime"
	"strings"
	"sync"
)

var fBlockSize = flag.Int("blockSize", 128*1024, "The block size used when reading files.")
var fVersion = flag.Bool("version", false, "Print the version number and exit.")
var fConcurrent = flag.Int("j", runtime.NumCPU(), "Maximum number of files processed concurrently.")
var fHash = flag.String("h", "sha256", "md5 sha1 sha224 sha256 sha384 sha512")

type fileInput struct {
	index    int
	fileName string
}

func main() {
	flag.Parse()
	*fHash = strings.ToLower(*fHash)

	if *fVersion {
		fmt.Println("gosum v1.0")
		fmt.Println("Copyright (c) 2014, Gregory L. Dietsche.")
		return
	}
	if *fConcurrent <= 0 {
		*fConcurrent = 1
	}

	in := make(chan fileInput, *fConcurrent*10)
	out := make(chan *string, *fConcurrent*10)

	go func() {
		for i, file := range flag.Args() {
			in <- fileInput{i, file}
		}
		close(in)
	}()

	go func() {
		var wg sync.WaitGroup
		for i := 0; i < *fConcurrent; i++ {
			wg.Add(1)
			var hash hash.Hash
			switch *fHash {
			case "md5":
				hash = md5.New()
			case "sha1":
				hash = sha1.New()
			case "sha224":
				hash = sha256.New224()
			case "sha256":
				hash = sha256.New()
			case "sha384":
				hash = sha512.New384()
			case "sha512":
				hash = sha512.New()
			default:
				panic("Unknown / unspported hash: " + *fHash)
			}
			go digester(&wg, &hash, out, in)
		}
		wg.Wait()
		close(out)
	}()

	for curResult := range out {
		fmt.Println(*curResult)
	}
}

func digester(wg *sync.WaitGroup, h *hash.Hash, out chan *string, files chan fileInput) {
	for file := range files {
		*fBlockSize = (*h).BlockSize()
		processFile(&file.fileName, *h)
		message := fmt.Sprintf("%d %08x\t%s", file.index, (*h).Sum(nil), file.fileName)
		out <- &message
	}
	wg.Done()
}

func processFile(filename *string, w io.Writer) (err error) {
	file, err := os.Open(*filename)
	if err != nil {
		return err
	}
	defer file.Close()

	r := bufio.NewReader(file)
	buffer := make([]byte, *fBlockSize)

	for {
		n, err := r.Read(buffer)
		if err != nil && err != io.EOF {
			return err
		}
		if n == 0 {
			break
		}

		if _, err := (w).Write(buffer[:n]); err != nil {
			panic(err.Error())
		}
	}
	return nil
}
<commit_msg>cleanup<commit_after>package main

import (
	"bufio"
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"flag"
	"fmt"
	"hash"
	"io"
	"os"
	"runtime"
	"strings"
	"sync"
)

var fBlockSize = flag.Int("blockSize", 128*1024, "The block size used when reading files.")
var fVersion = flag.Bool("version", false, "Print the version number and exit.")
var fConcurrent = flag.Int("j", runtime.NumCPU(), "Maximum number of files processed concurrently.")
var fHash = flag.String("h", "sha256", "md5 sha1 sha224 sha256 sha384 sha512")

type fileInput struct {
	index    int
	fileName string
}

func main() {
	flag.Parse()
	*fHash = strings.ToLower(*fHash)

	if *fVersion {
		fmt.Println("gosum v1.0")
		fmt.Println("Copyright (c) 2014, Gregory L. Dietsche.")
		return
	}
	if *fConcurrent <= 0 {
		*fConcurrent = 1
	}

	in := make(chan fileInput, *fConcurrent*10)
	out := make(chan *string, *fConcurrent*10)

	go func() {
		for i, file := range flag.Args() {
			in <- fileInput{i, file}
		}
		close(in)
	}()

	go func() {
		var wg sync.WaitGroup
		for i := 0; i < *fConcurrent; i++ {
			wg.Add(1)
			var hash hash.Hash
			switch *fHash {
			case "md5":
				hash = md5.New()
			case "sha1":
				hash = sha1.New()
			case "sha224":
				hash = sha256.New224()
			case "sha256":
				hash = sha256.New()
			case "sha384":
				hash = sha512.New384()
			case "sha512":
				hash = sha512.New()
			default:
				panic("Unknown / unspported hash: " + *fHash)
			}
			go digester(&wg, &hash, out, in)
		}
		wg.Wait()
		close(out)
	}()

	for curResult := range out {
		fmt.Println(*curResult)
	}
}

func digester(wg *sync.WaitGroup, h *hash.Hash, out chan *string, files chan fileInput) {
	for file := range files {
		//*fBlockSize = (*h).BlockSize()
		processFile(&file.fileName, *h)
		message := fmt.Sprintf("%d %08x\t%s", file.index, (*h).Sum(nil), file.fileName)
		out <- &message
	}
	wg.Done()
}

func processFile(filename *string, w io.Writer) (err error) {
	file, err := os.Open(*filename)
	if err != nil {
		return err
	}
	defer file.Close()

	r := bufio.NewReader(file)
	buffer := make([]byte, *fBlockSize)

	for {
		n, err := r.Read(buffer)
		if err != nil && err != io.EOF {
			return err
		}
		if n == 0 {
			break
		}

		if _, err := (w).Write(buffer[:n]); err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package gomapr

import (
	"errors"
	"log"
	"math/rand"
	"sync"
)

var (
	EndOfEmit = errors.New("Nothing left to emit")
)

type Event interface{}
type Partial interface{}
type ReduceKey interface{}

type MapReduce interface {
	Emit() (Event, error)
	Map(Event) (ReduceKey, Partial)
	Reduce(ReduceKey, []Partial) (ReduceKey, Partial)
}

// Corresponds to a set of values that a reducer can join.
type partialGroup struct {
	values []Partial
	l      *sync.Mutex
}

func newPartialGroup() *partialGroup {
	return &partialGroup{
		values: make([]Partial, 0),
		l:      &sync.Mutex{},
	}
}

// Adds a value to the group.
func (p *partialGroup) add(v interface{}) {
	p.values = append(p.values, v)
}

// Replaces the contents of the partial group.
func (p *partialGroup) replace(v interface{}) {
	p.values = []Partial{v}
}

// Contains all partial groups.
type reduceWorkspace struct {
	groups map[ReduceKey]*partialGroup
	l      *sync.Mutex
}

func newReduceWorkspace() *reduceWorkspace {
	return &reduceWorkspace{
		make(map[ReduceKey]*partialGroup),
		&sync.Mutex{},
	}
}

// Returns a partial group by its key.
func (r *reduceWorkspace) getPartialGroup(key ReduceKey) *partialGroup {
	r.l.Lock()
	defer r.l.Unlock()

	partialGroup, ok := r.groups[key]
	if !ok {
		partialGroup = newPartialGroup()
		r.groups[key] = partialGroup
	}

	return partialGroup
}

// Adds a key-value pair to its appropriate partial group.
func (r *reduceWorkspace) add(key ReduceKey, value Partial) {
	partialGroup := r.getPartialGroup(key)

	partialGroup.l.Lock()
	partialGroup.add(value)
	partialGroup.l.Unlock()
}

// Replaces an existing partial group with the input arguments.
func (r *reduceWorkspace) replace(key ReduceKey, value Partial) {
	partialGroup := r.getPartialGroup(key)

	partialGroup.l.Lock()
	partialGroup.replace(value)
	partialGroup.l.Unlock()
}

// Contains configuration for a MapReduce task.
type Runner struct {
	reduceWorkspace *reduceWorkspace
	mr              MapReduce
	mapWg           *sync.WaitGroup
	reduceWg        *sync.WaitGroup
	mappers         int
	reduceFactor    float64
	unreduced       map[ReduceKey]struct{}
	unreducedL      *sync.Mutex
}

func NewRunner(mr MapReduce, mappers int, reduceFactor float64) *Runner {
	if reduceFactor < 0 || reduceFactor > 1 {
		panic("Invalid reduce factor")
	}

	return &Runner{
		reduceWorkspace: newReduceWorkspace(),
		mr:              mr,
		mapWg:           &sync.WaitGroup{},
		reduceWg:        &sync.WaitGroup{},
		mappers:         mappers,
		reduceFactor:    reduceFactor,
		unreduced:       make(map[ReduceKey]struct{}),
		unreducedL:      &sync.Mutex{},
	}
}

// Returns the map containing all groups. Only safe to call after
// the task has completed.
func (r *Runner) Groups() map[ReduceKey]Partial {
	groups := make(map[ReduceKey]Partial)

	for k, v := range r.reduceWorkspace.groups {
		groups[k] = v.values[0]
	}

	return groups
}

// Returns the slice containing all groups. Only safe to call after
// the task has completed.
func (r *Runner) Results() ResultSlice {
	groups := make([]*Result, 0)

	for k, v := range r.reduceWorkspace.groups {
		groups = append(
			groups,
			&Result{k, v.values[0]},
		)
	}

	return groups
}

// Maps the input it receives on its emitted channel, spawning
// a reduce task when appropriate.
func (r *Runner) mapWorker(emitted chan Event) {
	for val := range emitted {
		key, mapped := r.mr.Map(val)
		r.reduceWorkspace.add(key, mapped)

		// Launch reduce step probabilistically.
		if rand.Float64() < r.reduceFactor {
			r.unreducedL.Lock()
			delete(r.unreduced, key)
			r.unreducedL.Unlock()

			r.reduceWg.Add(1)
			go r.reduce(key)
		} else {
			r.unreducedL.Lock()
			r.unreduced[key] = struct{}{}
			r.unreducedL.Unlock()
		}
	}

	r.mapWg.Done()
}

// Reduces the partial group with the matching input key.
func (r *Runner) reduce(key ReduceKey) {
	partialGroup := r.reduceWorkspace.getPartialGroup(key)

	partialGroup.l.Lock()
	defer partialGroup.l.Unlock()

	if len(partialGroup.values) > 1 {
		newKey, partial := r.mr.Reduce(key, partialGroup.values)

		if key == newKey {
			partialGroup.replace(partial)
		} else {
			r.reduceWorkspace.replace(key, partial)
		}
	}

	r.reduceWg.Done()
}

// Starts the MapReduce task.
func (r *Runner) Run() {
	events := make(chan Event, r.mappers)

	// Create background mapping workers.
	for i := 0; i < r.mappers; i++ {
		r.mapWg.Add(1)
		go r.mapWorker(events)
	}

	// Emit all events.
	go func() {
		for {
			event, err := r.mr.Emit()
			if err != EndOfEmit && err != nil {
				log.Printf("Error emitting: %v", err)
				break
			}

			events <- event

			if err == EndOfEmit {
				break
			}

		}
		close(events)
	}()

	r.mapWg.Wait()

	// Reduce unreduced keys.
	for key, _ := range r.unreduced {
		r.reduceWg.Add(1)
		go r.reduce(key)
	}
	r.unreduced = make(map[ReduceKey]struct{})
	r.reduceWg.Wait()
}

// Runs MapReduce synchronously.
func (r *Runner) RunSynchronous() {
	for {
		event, err := r.mr.Emit()
		if err != EndOfEmit && err != nil {
			log.Printf("Error emitting: %v", err)
			break
		}

		key, mapped := r.mr.Map(event)
		r.reduceWorkspace.add(key, mapped)

		if err == EndOfEmit {
			break
		}

	}

	for key, _ := range r.reduceWorkspace.groups {
		r.reduceWg.Add(1)
		r.reduce(key)
	}
}
<commit_msg>Use embedded mutex<commit_after>package gomapr

import (
	"errors"
	"log"
	"math/rand"
	"sync"
)

var (
	EndOfEmit = errors.New("Nothing left to emit")
)

type Event interface{}
type Partial interface{}
type ReduceKey interface{}

type MapReduce interface {
	Emit() (Event, error)
	Map(Event) (ReduceKey, Partial)
	Reduce(ReduceKey, []Partial) (ReduceKey, Partial)
}

// Corresponds to a set of values that a reducer can join.
type partialGroup struct {
	sync.Mutex
	values []Partial
}

func newPartialGroup() *partialGroup {
	return &partialGroup{
		values: make([]Partial, 0),
	}
}

// Adds a value to the group.
func (p *partialGroup) add(v interface{}) {
	p.values = append(p.values, v)
}

// Replaces the contents of the partial group.
func (p *partialGroup) replace(v interface{}) {
	p.values = []Partial{v}
}

// Contains all partial groups.
type reduceWorkspace struct {
	sync.Mutex
	groups map[ReduceKey]*partialGroup
}

func newReduceWorkspace() *reduceWorkspace {
	return &reduceWorkspace{
		groups: make(map[ReduceKey]*partialGroup),
	}
}

// Returns a partial group by its key.
func (r *reduceWorkspace) getPartialGroup(key ReduceKey) *partialGroup {
	r.Lock()
	defer r.Unlock()

	partialGroup, ok := r.groups[key]
	if !ok {
		partialGroup = newPartialGroup()
		r.groups[key] = partialGroup
	}

	return partialGroup
}

// Adds a key-value pair to its appropriate partial group.
func (r *reduceWorkspace) add(key ReduceKey, value Partial) {
	partialGroup := r.getPartialGroup(key)

	partialGroup.Lock()
	partialGroup.add(value)
	partialGroup.Unlock()
}

// Replaces an existing partial group with the input arguments.
func (r *reduceWorkspace) replace(key ReduceKey, value Partial) {
	partialGroup := r.getPartialGroup(key)

	partialGroup.Lock()
	partialGroup.replace(value)
	partialGroup.Unlock()
}

// Contains configuration for a MapReduce task.
type Runner struct {
	reduceWorkspace *reduceWorkspace
	mr              MapReduce
	mapWg           *sync.WaitGroup
	reduceWg        *sync.WaitGroup
	mappers         int
	reduceFactor    float64
	unreduced       map[ReduceKey]struct{}
	unreducedL      *sync.Mutex
}

func NewRunner(mr MapReduce, mappers int, reduceFactor float64) *Runner {
	if reduceFactor < 0 || reduceFactor > 1 {
		panic("Invalid reduce factor")
	}

	return &Runner{
		reduceWorkspace: newReduceWorkspace(),
		mr:              mr,
		mapWg:           &sync.WaitGroup{},
		reduceWg:        &sync.WaitGroup{},
		mappers:         mappers,
		reduceFactor:    reduceFactor,
		unreduced:       make(map[ReduceKey]struct{}),
		unreducedL:      &sync.Mutex{},
	}
}

// Returns the map containing all groups. Only safe to call after
// the task has completed.
func (r *Runner) Groups() map[ReduceKey]Partial {
	groups := make(map[ReduceKey]Partial)

	for k, v := range r.reduceWorkspace.groups {
		groups[k] = v.values[0]
	}

	return groups
}

// Returns the slice containing all groups. Only safe to call after
// the task has completed.
func (r *Runner) Results() ResultSlice {
	groups := make([]*Result, 0)

	for k, v := range r.reduceWorkspace.groups {
		groups = append(
			groups,
			&Result{k, v.values[0]},
		)
	}

	return groups
}

// Maps the input it receives on its emitted channel, spawning
// a reduce task when appropriate.
func (r *Runner) mapWorker(emitted chan Event) {
	for val := range emitted {
		key, mapped := r.mr.Map(val)
		r.reduceWorkspace.add(key, mapped)

		// Launch reduce step probabilistically.
		if rand.Float64() < r.reduceFactor {
			r.unreducedL.Lock()
			delete(r.unreduced, key)
			r.unreducedL.Unlock()

			r.reduceWg.Add(1)
			go r.reduce(key)
		} else {
			r.unreducedL.Lock()
			r.unreduced[key] = struct{}{}
			r.unreducedL.Unlock()
		}
	}

	r.mapWg.Done()
}

// Reduces the partial group with the matching input key.
func (r *Runner) reduce(key ReduceKey) {
	partialGroup := r.reduceWorkspace.getPartialGroup(key)

	partialGroup.Lock()
	defer partialGroup.Unlock()

	if len(partialGroup.values) > 1 {
		newKey, partial := r.mr.Reduce(key, partialGroup.values)

		if key == newKey {
			partialGroup.replace(partial)
		} else {
			r.reduceWorkspace.replace(key, partial)
		}
	}

	r.reduceWg.Done()
}

// Starts the MapReduce task.
func (r *Runner) Run() {
	events := make(chan Event, r.mappers)

	// Create background mapping workers.
	for i := 0; i < r.mappers; i++ {
		r.mapWg.Add(1)
		go r.mapWorker(events)
	}

	// Emit all events.
	go func() {
		for {
			event, err := r.mr.Emit()
			if err != EndOfEmit && err != nil {
				log.Printf("Error emitting: %v", err)
				break
			}

			events <- event

			if err == EndOfEmit {
				break
			}

		}
		close(events)
	}()

	r.mapWg.Wait()

	// Reduce unreduced keys.
	for key, _ := range r.unreduced {
		r.reduceWg.Add(1)
		go r.reduce(key)
	}
	r.unreduced = make(map[ReduceKey]struct{})
	r.reduceWg.Wait()
}

// Runs MapReduce synchronously.
func (r *Runner) RunSynchronous() {
	for {
		event, err := r.mr.Emit()
		if err != EndOfEmit && err != nil {
			log.Printf("Error emitting: %v", err)
			break
		}

		key, mapped := r.mr.Map(event)
		r.reduceWorkspace.add(key, mapped)

		if err == EndOfEmit {
			break
		}

	}

	for key, _ := range r.reduceWorkspace.groups {
		r.reduceWg.Add(1)
		r.reduce(key)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"log/syslog"
	"os"
	"strconv"
	"strings"

	"github.com/dekelund/stdres"
	"github.com/urfave/cli/v2"

	"gomate.io/gomate/compiler/definition"
	"gomate.io/gomate/compiler/feature"
	"gomate.io/gomate/internal/highlighter"
	"gomate.io/gomate/logging"
)

const (
	pathSeparator = string(os.PathSeparator)
)

var settings struct {
	SysLog     logging.Settings
	Forensic   bool
	PPrint     bool
	CWD        string
	DefPattern string
}

var cwd = "."

func init() {
	settings.SysLog.Priority = syslog.LOG_INFO

	var err error

	if cwd, err = os.Getwd(); err != nil {
		logging.Fatal(err.Error())
	}
}

func main() {
	app := cli.NewApp()
	app.Name = "gomate"
	app.Version = "v0.3.0"
	app.Usage = "Run behaviour driven tests as Gherik features"
	app.Flags = []cli.Flag{
		&cli.BoolFlag{
			Name:  "syslog",
			Usage: "Redirect STDOUT to SysLog server",
		},
		&cli.BoolFlag{
			Name:  "syslog-udp",
			Usage: "Use UDP instead of TCP",
		},
		&cli.StringFlag{
			Name:  "syslog-raddr",
			Usage: "HOST/IP address to SysLog server",
			Value: "localhost",
		},
		&cli.StringFlag{
			Name:  "syslog-tag",
			Usage: "Tag output with specified text string",
			Value: "gomate",
		},
		&cli.IntFlag{
			Name: "priority",
			Usage: "Log priority, use bitwised values from /usr/include/sys/syslog.h e.g.," +
				" LOG_EMERG=" + strconv.Itoa(int(syslog.LOG_EMERG)) +
				" LOG_ALERT=" + strconv.Itoa(int(syslog.LOG_ALERT)) +
				" LOG_CRIT=" + strconv.Itoa(int(syslog.LOG_CRIT)) +
				" LOG_ERR=" + strconv.Itoa(int(syslog.LOG_ERR)) +
				" LOG_WARNING=" + strconv.Itoa(int(syslog.LOG_WARNING)) +
				" LOG_NOTICE=" + strconv.Itoa(int(syslog.LOG_NOTICE)) +
				" LOG_INFO=" + strconv.Itoa(int(syslog.LOG_INFO)) +
				" LOG_DEBUG=" + strconv.Itoa(int(syslog.LOG_DEBUG)),
			Value: int(syslog.LOG_INFO),
		},
		&cli.BoolFlag{
			Name:  "pretty",
			Usage: "Print colorised result to STDOUT/STDERR",
		},
		&cli.BoolFlag{
			Name:  "forensic",
			Usage: "A kind of development mode, all generated files will be kept",
		},
		&cli.StringFlag{
			Name:  "step-definitions",
			Value: "step_definitions",
			Usage: "Definitions folder name, should be located in features folder",
		},
		&cli.StringFlag{
			Name:  "dir",
			Value: ".",
			Usage: "Relative path, to a feature-file or -directory (Current value: " + cwd + ").",
		},
	}

	app.Commands = []*cli.Command{{
		Name:    "feature-files",
		Aliases: []string{},
		Usage:   "List feature files to STDOUT",
		Flags:   []cli.Flag{},
		Action:  listFeatureFilesCMD,
	}, {
		Name:    "features",
		Aliases: []string{},
		Usage:   "List features to STDOUT",
		Flags:   []cli.Flag{},
		Action:  listFeaturesCMD,
	}, {
		Name:    "definitions",
		Aliases: []string{"defs", "code"},
		Usage:   "List behaviours to STDOUT",
		Flags:   []cli.Flag{},
		Action:  printDefinitionsCodeCMD,
	}, {
		Name:    "test",
		Aliases: []string{"t"},
		Usage:   "Tests either a test directory with features in it, or a .feature file",
		Flags:   []cli.Flag{},
		Action:  testCMD,
	}}

	if err := app.Run(os.Args); err != nil {
		fmt.Printf("exiting due to unexpected error: %s", err)
	}
}

func setupGlobals(c *cli.Context) {
	settings.CWD = cwd

	settings.SysLog = logging.Settings{
		Active:   c.Bool("syslog"),
		UDP:      c.Bool("syslog-udp"),
		RAddr:    c.String("syslog-raddr"),
		Tag:      c.String("syslog-tag"),
		Priority: syslog.Priority(c.Int("priority")),
	}

	settings.PPrint = c.Bool("pretty")
	settings.Forensic = c.Bool("forensic")
	settings.DefPattern = c.String("step-definitions")

	if settings.PPrint {
		stdres.EnableColor()
	} else {
		stdres.DisableColor()
	}

	logging.ReconfigureLogger(settings.SysLog)
}

func listFeatureFilesCMD(c *cli.Context) error {
	setupGlobals(c)
	dir := c.String("dir")

	_, features := parseDir(dir)

	for i, feature := range features {
		path := cwd + pathSeparator
		logging.Infof("\t%2d) %s\n", i, strings.TrimPrefix(feature, path))
	}

	return nil
}

func listFeaturesCMD(c *cli.Context) error {
	setupGlobals(c)
	dir := c.String("dir")

	_, features := parseDir(dir)

	// #nosec
	for _, feature := range features {
		fileReader, err := os.Open(feature)
		if err != nil {
			logging.Fatal(err.Error())
		}

		bytes, err := ioutil.ReadAll(fileReader)
		if err != nil {
			logging.Fatal(err.Error())
		}

		text := string(bytes)

		if settings.PPrint {
			text = highlighter.Feature(text)
		}

		path := cwd + pathSeparator
		logging.Infof("\n# %s\n%s\n", strings.TrimPrefix(feature, path), text)
	}

	return nil
}

func printDefinitionsCodeCMD(c *cli.Context) error {
	setupGlobals(c)
	dir := c.String("dir")

	definitions, _ := parseDir(dir)

	defs := definitions.Code()

	if settings.PPrint {
		defs = highlighter.Definition(defs)
	}

	logging.Infof(defs)

	return nil
}

// testCMD search, compile and execute features defined in Gherik format where behaviours are defined in Go-Lang based files.
// Behaviours might be undefined, which will end up as red text in stdout if the context c has pretty print enabled.
func testCMD(c *cli.Context) error {
	setupGlobals(c)
	dir := c.String("dir")

	definitions, features := parseDir(dir)

	if !settings.Forensic {
		defer definitions.Remove()
	}

	// #nosec
	for _, file := range features {
		fd, err := os.Open(file)
		if err != nil {
			logging.Fatal(err.Error())
		}
		defer fd.Close()

		definitions.Run(fd, settings.PPrint)
	}

	return nil
}

func parseDir(path string) (definition.Definitions, []string) {
	var err error
	var list = feature.List{}
	var defs = []io.Reader{}

	if list, err = feature.ParseDir(path, settings.DefPattern); err != nil {
		logging.Fatal(err.Error())
	}

	// #nosec
	for _, def := range list.Definitions {
		file, err := os.Open(def)
		if err != nil {
			logging.Fatal(err.Error())
		}

		defs = append(defs, io.Reader(file))
		defer file.Close()
	}

	return definition.NewDefinitions(defs, settings.Forensic), list.Features
}
<commit_msg>VERSION: v0.2.1<commit_after>package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"log/syslog"
	"os"
	"strconv"
	"strings"

	"github.com/dekelund/stdres"
	"github.com/urfave/cli/v2"

	"gomate.io/gomate/compiler/definition"
	"gomate.io/gomate/compiler/feature"
	"gomate.io/gomate/internal/highlighter"
	"gomate.io/gomate/logging"
)

const (
	pathSeparator = string(os.PathSeparator)
)

var settings struct {
	SysLog     logging.Settings
	Forensic   bool
	PPrint     bool
	CWD        string
	DefPattern string
}

var cwd = "."

func init() {
	settings.SysLog.Priority = syslog.LOG_INFO

	var err error

	if cwd, err = os.Getwd(); err != nil {
		logging.Fatal(err.Error())
	}
}

func main() {
	app := cli.NewApp()
	app.Name = "gomate"
	app.Version = "v0.2.1"
	app.Usage = "Run behaviour driven tests as Gherik features"
	app.Flags = []cli.Flag{
		&cli.BoolFlag{
			Name:  "syslog",
			Usage: "Redirect STDOUT to SysLog server",
		},
		&cli.BoolFlag{
			Name:  "syslog-udp",
			Usage: "Use UDP instead of TCP",
		},
		&cli.StringFlag{
			Name:  "syslog-raddr",
			Usage: "HOST/IP address to SysLog server",
			Value: "localhost",
		},
		&cli.StringFlag{
			Name:  "syslog-tag",
			Usage: "Tag output with specified text string",
			Value: "gomate",
		},
		&cli.IntFlag{
			Name: "priority",
			Usage: "Log priority, use bitwised values from /usr/include/sys/syslog.h e.g.," +
				" LOG_EMERG=" + strconv.Itoa(int(syslog.LOG_EMERG)) +
				" LOG_ALERT=" + strconv.Itoa(int(syslog.LOG_ALERT)) +
				" LOG_CRIT=" + strconv.Itoa(int(syslog.LOG_CRIT)) +
				" LOG_ERR=" + strconv.Itoa(int(syslog.LOG_ERR)) +
				" LOG_WARNING=" + strconv.Itoa(int(syslog.LOG_WARNING)) +
				" LOG_NOTICE=" + strconv.Itoa(int(syslog.LOG_NOTICE)) +
				" LOG_INFO=" + strconv.Itoa(int(syslog.LOG_INFO)) +
				" LOG_DEBUG=" + strconv.Itoa(int(syslog.LOG_DEBUG)),
			Value: int(syslog.LOG_INFO),
		},
		&cli.BoolFlag{
			Name:  "pretty",
			Usage: "Print colorised result to STDOUT/STDERR",
		},
		&cli.BoolFlag{
			Name:  "forensic",
			Usage: "A kind of development mode, all generated files will be kept",
		},
		&cli.StringFlag{
			Name:  "step-definitions",
			Value: "step_definitions",
			Usage: "Definitions folder name, should be located in features folder",
		},
		&cli.StringFlag{
			Name:  "dir",
			Value: ".",
			Usage: "Relative path, to a feature-file or -directory (Current value: " + cwd + ").",
		},
	}

	app.Commands = []*cli.Command{{
		Name:    "feature-files",
		Aliases: []string{},
		Usage:   "List feature files to STDOUT",
		Flags:   []cli.Flag{},
		Action:  listFeatureFilesCMD,
	}, {
		Name:    "features",
		Aliases: []string{},
		Usage:   "List features to STDOUT",
		Flags:   []cli.Flag{},
		Action:  listFeaturesCMD,
	}, {
		Name:    "definitions",
		Aliases: []string{"defs", "code"},
		Usage:   "List behaviours to STDOUT",
		Flags:   []cli.Flag{},
		Action:  printDefinitionsCodeCMD,
	}, {
		Name:    "test",
		Aliases: []string{"t"},
		Usage:   "Tests either a test directory with features in it, or a .feature file",
		Flags:   []cli.Flag{},
		Action:  testCMD,
	}}

	if err := app.Run(os.Args); err != nil {
		fmt.Printf("exiting due to unexpected error: %s", err)
	}
}

func setupGlobals(c *cli.Context) {
	settings.CWD = cwd

	settings.SysLog = logging.Settings{
		Active:   c.Bool("syslog"),
		UDP:      c.Bool("syslog-udp"),
		RAddr:    c.String("syslog-raddr"),
		Tag:      c.String("syslog-tag"),
		Priority: syslog.Priority(c.Int("priority")),
	}

	settings.PPrint = c.Bool("pretty")
	settings.Forensic = c.Bool("forensic")
	settings.DefPattern = c.String("step-definitions")

	if settings.PPrint {
		stdres.EnableColor()
	} else {
		stdres.DisableColor()
	}

	logging.ReconfigureLogger(settings.SysLog)
}

func listFeatureFilesCMD(c *cli.Context) error {
	setupGlobals(c)
	dir := c.String("dir")

	_, features := parseDir(dir)

	for i, feature := range features {
		path := cwd + pathSeparator
		logging.Infof("\t%2d) %s\n", i, strings.TrimPrefix(feature, path))
	}

	return nil
}

func listFeaturesCMD(c *cli.Context) error {
	setupGlobals(c)
	dir := c.String("dir")

	_, features := parseDir(dir)

	// #nosec
	for _, feature := range features {
		fileReader, err := os.Open(feature)
		if err != nil {
			logging.Fatal(err.Error())
		}

		bytes, err := ioutil.ReadAll(fileReader)
		if err != nil {
			logging.Fatal(err.Error())
		}

		text := string(bytes)

		if settings.PPrint {
			text = highlighter.Feature(text)
		}

		path := cwd + pathSeparator
		logging.Infof("\n# %s\n%s\n", strings.TrimPrefix(feature, path), text)
	}

	return nil
}

func printDefinitionsCodeCMD(c *cli.Context) error {
	setupGlobals(c)
	dir := c.String("dir")

	definitions, _ := parseDir(dir)

	defs := definitions.Code()

	if settings.PPrint {
		defs = highlighter.Definition(defs)
	}

	logging.Infof(defs)

	return nil
}

// testCMD search, compile and execute features defined in Gherik format where behaviours are defined in Go-Lang based files.
// Behaviours might be undefined, which will end up as red text in stdout if the context c has pretty print enabled.
func testCMD(c *cli.Context) error {
	setupGlobals(c)
	dir := c.String("dir")

	definitions, features := parseDir(dir)

	if !settings.Forensic {
		defer definitions.Remove()
	}

	// #nosec
	for _, file := range features {
		fd, err := os.Open(file)
		if err != nil {
			logging.Fatal(err.Error())
		}
		defer fd.Close()

		definitions.Run(fd, settings.PPrint)
	}

	return nil
}

func parseDir(path string) (definition.Definitions, []string) {
	var err error
	var list = feature.List{}
	var defs = []io.Reader{}

	if list, err = feature.ParseDir(path, settings.DefPattern); err != nil {
		logging.Fatal(err.Error())
	}

	// #nosec
	for _, def := range list.Definitions {
		file, err := os.Open(def)
		if err != nil {
			logging.Fatal(err.Error())
		}

		defs = append(defs, io.Reader(file))
		defer file.Close()
	}

	return definition.NewDefinitions(defs, settings.Forensic), list.Features
}
<|endoftext|>


<commit_before>package goober

import  (
  "net/http"
  "strings"
  "io"
  "time"
  "fmt"
)

// Main goober struct. Abides the handler interface.
type Goober struct {
  head map[string]*routeTreeNode
  ErrorPages map[int]string
}

// Goober handlers, for simplicity, are just functions with a given
// signature.
type Handler func(http.ResponseWriter, *Request)

// We use this a few places, so we can give it a type as well.
type RouteMap map[string]*routeTreeNode

// Our parse tree structure for routes
type routeTreeNode struct {
  handler Handler // Handler if a node is a terminal
  children RouteMap // Static children
  variables RouteMap // Dynamic/variable children
}

// Augment http.Request with URLParams that will be grabbed
// from the request in the form of /:variables/
type Request struct {
  http.Request
  URLParams map[string]string
}

// A quick initializer for routeTreeNodes
func newRouteTreeNode() (node *routeTreeNode) {
  node = &routeTreeNode{
    children: make(RouteMap),
    variables: make(RouteMap),
  }

  return
}

// Initialize our Goober object
func New() (* Goober) {
  var head = make(RouteMap)
  head["GET"] = newRouteTreeNode()
  head["HEAD"] = newRouteTreeNode()
  head["POST"] = newRouteTreeNode()
  head["PUT"] = newRouteTreeNode()
  head["DELETE"] = newRouteTreeNode()

  g := &Goober{
    head: head,
    ErrorPages: make(map[int]string),
  }

  return g
}

// Simple helper to allow us to trim leading and trailing /'s
func isSlash(s rune) (bool) {
  return s == '/'
}

type BadRouteError struct {
  Route string
  Reason string
}

func (e BadRouteError) Error() string {
  return "\"" + e.Route + "\" is an invalid route because " + e.Reason + "."
}

// Adds a handler to our route tree
func (g *Goober) AddHandler(method string, route string, handler Handler) (err error){
  err = nil
  route = strings.TrimFunc(route, isSlash)
  var parts = strings.Split(route, "/")

  // Iterate through the bits of our path and add to the tree
  var cur = g.head[method]
  for i := range parts {
    var part = parts[i]

    // No // empty paths
    if (len(part) == 0) {
      err := BadRouteError{
        Route: route,
        Reason: "it had an empty segment",
      }
      return err
    }

    // Check for variables
    if strings.HasPrefix(part, ":") {
      // dynamic
      if (cur.variables[part] != nil) {
        cur = cur.variables[part]
      } else {
        cur.variables[part] = newRouteTreeNode()
        cur = cur.variables[part]
      }
    } else {
      // static
      if (cur.children[part] != nil) {
        cur = cur.children[part]
      } else {
        cur.children[part] = newRouteTreeNode()
        cur = cur.children[part]
      }
    }
  }

  // add handler
  cur.handler = handler
  return
}

// Wrapper functions for common types of request
func (g *Goober) Get(route string, handler Handler) (error) {
  return g.AddHandler("GET", route, handler)
  return g.AddHandler("HEAD", route, handler)
}

func (g *Goober) Post(route string, handler Handler) (error) {
  return g.AddHandler("POST", route, handler)
}

func (g *Goober) Put(route string, handler Handler) (error) {
  return g.AddHandler("PUT", route, handler)
}

func (g *Goober) Delete(route string, handler Handler) (error) {
  return g.AddHandler("DELETE", route, handler)
}

type RouteNotFoundError struct {
  Route string
}

func (e RouteNotFoundError) Error() string {
  return "Route \"" + e.Route + "\" was not found."
}

func walkTree(node *routeTreeNode, parts []string, r *Request) (handler Handler, err error) {
  err = nil
  handler = nil

  if len(parts) == 0 {
    // if we've reached a terminal state, return handler
    handler = node.handler
    if handler == nil {
      err = &RouteNotFoundError{Route: r.URL.Path}
    }
  } else {
    // else, look for it
    var part = parts[0]

    if child, ok := node.children["*"]; ok {
      handler = child.handler
      r.URLParams["*"] = strings.Join(parts, "/")
    } else if node.children[part] != nil {
      // check static routes first, they have priority
      return walkTree(node.children[part], parts[1:], r)
    } else {
      for k, v := range node.variables {
        // check all dynamic routes, taking first match
        handler, err = walkTree(v, parts[1:], r)
        if err == nil {
          // goofy recursive way to build up params
          r.URLParams[k] = part
          return
        }
      }

      // if we don't find any dynamic matches, there was an error
      err = &RouteNotFoundError{Route: r.URL.Path}
    }
  }

  return
}

// Given a request, find the appropriate handler
func (g *Goober) GetHandler(r *Request) (handler Handler, err error) {
  var path = strings.TrimFunc(r.URL.Path, isSlash)
  var parts = strings.Split(path, "/")
  return walkTree(g.head[r.Method], parts, r)
}

// A simple function to handle error pages for us
func (g *Goober) errorHandler(w http.ResponseWriter, r *Request, code int) {
  if page, ok := g.ErrorPages[code]; ok {
    w.Header().Set("Content-Type", "text/html; charset=utf-8")
    w.WriteHeader(code)
    io.WriteString(w, page)
  }
}

// Routes requests
func (g *Goober) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  var startTime = time.Now()
  defer func() {
    fmt.Printf("[%s] %s - took %s\n", r.Method, r.URL.Path, time.Since(startTime))
    r.Body.Close()
  }()

  // create augmented request object
  var request = &Request{
    Request: *r,
    URLParams: make(map[string]string),
  }

  // get the handler for the request
  var f, err = g.GetHandler(request)
  if err == nil {
    // user response. pad with content-type.
    w.Header().Set("Content-Type", "text/html; charset=utf-8")
    f(w, request)
  } else {
    fmt.Println("[ERROR] " + err.Error())
    g.errorHandler(w, request, 404)
  }

}

// shortcut to start serving a goober service
func (g *Goober) ListenAndServe(addr string) (err error)  {
  http.Handle("/", g)
  return http.ListenAndServe(addr, nil)
}

<commit_msg>Added extra headers per artem's pull request<commit_after>package goober

import  (
  "net/http"
  "strings"
  "io"
  "time"
  "fmt"
)

// Main goober struct. Abides the handler interface.
type Goober struct {
  head map[string]*routeTreeNode
  ErrorPages map[int]string
}

// Goober handlers, for simplicity, are just functions with a given
// signature.
type Handler func(http.ResponseWriter, *Request)

// We use this a few places, so we can give it a type as well.
type RouteMap map[string]*routeTreeNode

// Our parse tree structure for routes
type routeTreeNode struct {
  handler Handler // Handler if a node is a terminal
  children RouteMap // Static children
  variables RouteMap // Dynamic/variable children
}

// Augment http.Request with URLParams that will be grabbed
// from the request in the form of /:variables/
type Request struct {
  http.Request
  URLParams map[string]string
}

// A quick initializer for routeTreeNodes
func newRouteTreeNode() (node *routeTreeNode) {
  node = &routeTreeNode{
    children: make(RouteMap),
    variables: make(RouteMap),
  }

  return
}

// Initialize our Goober object
func New() (* Goober) {
  var head = make(RouteMap)
  head["GET"] = newRouteTreeNode()
  head["HEAD"] = newRouteTreeNode()
  head["POST"] = newRouteTreeNode()
  head["PUT"] = newRouteTreeNode()
  head["DELETE"] = newRouteTreeNode()

  g := &Goober{
    head: head,
    ErrorPages: make(map[int]string),
  }

  return g
}

// Simple helper to allow us to trim leading and trailing /'s
func isSlash(s rune) (bool) {
  return s == '/'
}

type BadRouteError struct {
  Route string
  Reason string
}

func (e BadRouteError) Error() string {
  return "\"" + e.Route + "\" is an invalid route because " + e.Reason + "."
}

// Adds a handler to our route tree
func (g *Goober) AddHandler(method string, route string, handler Handler) (err error){
  err = nil
  route = strings.TrimFunc(route, isSlash)
  var parts = strings.Split(route, "/")

  // Iterate through the bits of our path and add to the tree
  var cur = g.head[method]
  for i := range parts {
    var part = parts[i]

    // No // empty paths
    if (len(part) == 0) {
      err := BadRouteError{
        Route: route,
        Reason: "it had an empty segment",
      }
      return err
    }

    // Check for variables
    if strings.HasPrefix(part, ":") {
      // dynamic
      if (cur.variables[part] != nil) {
        cur = cur.variables[part]
      } else {
        cur.variables[part] = newRouteTreeNode()
        cur = cur.variables[part]
      }
    } else {
      // static
      if (cur.children[part] != nil) {
        cur = cur.children[part]
      } else {
        cur.children[part] = newRouteTreeNode()
        cur = cur.children[part]
      }
    }
  }

  // add handler
  cur.handler = handler
  return
}

// Wrapper functions for common types of request
func (g *Goober) Get(route string, handler Handler) (error) {
  return g.AddHandler("GET", route, handler)
  return g.AddHandler("HEAD", route, handler)
}

func (g *Goober) Post(route string, handler Handler) (error) {
  return g.AddHandler("POST", route, handler)
}

func (g *Goober) Put(route string, handler Handler) (error) {
  return g.AddHandler("PUT", route, handler)
}

func (g *Goober) Delete(route string, handler Handler) (error) {
  return g.AddHandler("DELETE", route, handler)
}

type RouteNotFoundError struct {
  Route string
}

func (e RouteNotFoundError) Error() string {
  return "Route \"" + e.Route + "\" was not found."
}

func walkTree(node *routeTreeNode, parts []string, r *Request) (handler Handler, err error) {
  err = nil
  handler = nil

  if len(parts) == 0 {
    // if we've reached a terminal state, return handler
    handler = node.handler
    if handler == nil {
      err = &RouteNotFoundError{Route: r.URL.Path}
    }
  } else {
    // else, look for it
    var part = parts[0]

    if child, ok := node.children["*"]; ok {
      handler = child.handler
      r.URLParams["*"] = strings.Join(parts, "/")
    } else if node.children[part] != nil {
      // check static routes first, they have priority
      return walkTree(node.children[part], parts[1:], r)
    } else {
      for k, v := range node.variables {
        // check all dynamic routes, taking first match
        handler, err = walkTree(v, parts[1:], r)
        if err == nil {
          // goofy recursive way to build up params
          r.URLParams[k] = part
          return
        }
      }

      // if we don't find any dynamic matches, there was an error
      err = &RouteNotFoundError{Route: r.URL.Path}
    }
  }

  return
}

// Given a request, find the appropriate handler
func (g *Goober) GetHandler(r *Request) (handler Handler, err error) {
  var path = strings.TrimFunc(r.URL.Path, isSlash)
  var parts = strings.Split(path, "/")
  return walkTree(g.head[r.Method], parts, r)
}

// A simple function to handle error pages for us
func (g *Goober) errorHandler(w http.ResponseWriter, r *Request, code int) {
  if page, ok := g.ErrorPages[code]; ok {
    w.Header().Set("Content-Type", "text/html; charset=utf-8")
    w.WriteHeader(code)
    io.WriteString(w, page)
  }
}

// Borrowed from web.go
func webTime(t time.Time) string {
  ftime := t.Format(time.RFC1123)
  if strings.HasSuffix(ftime, "UTC") {
    ftime = ftime[0:len(ftime)-3] + "GMT"
  }
  return ftime
}

// Routes requests
func (g *Goober) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  var startTime = time.Now()
  defer func() {
    fmt.Printf("[%s] %s - took %s\n", r.Method, r.URL.Path, time.Since(startTime))
    r.Body.Close()
  }()

  // create augmented request object
  var request = &Request{
    Request: *r,
    URLParams: make(map[string]string),
  }

  // get the handler for the request
  var f, err = g.GetHandler(request)
  if err == nil {
    // user response. pad with content-type.
    w.Header().Set("Content-Type", "text/html; charset=utf-8")
    w.Header().Set("Server", "goober.go")
    w.Header().Set("Date", webTime(time.Now().UTC()))
    f(w, request)
  } else {
    fmt.Println("[ERROR] " + err.Error())
    g.errorHandler(w, request, 404)
  }

}

// shortcut to start serving a goober service
func (g *Goober) ListenAndServe(addr string) (err error)  {
  http.Handle("/", g)
  return http.ListenAndServe(addr, nil)
}

<|endoftext|>