<commit_before>package cloudwatch

import (
	"log"
	"math"
	"sort"
	"strings"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/cloudwatch"

	"github.com/influxdata/telegraf"
	internalaws "github.com/influxdata/telegraf/internal/config/aws"
	"github.com/influxdata/telegraf/plugins/outputs"
)

type CloudWatch struct {
	Region    string `toml:"region"`
	AccessKey string `toml:"access_key"`
	SecretKey string `toml:"secret_key"`
	RoleARN   string `toml:"role_arn"`
	Profile   string `toml:"profile"`
	Filename  string `toml:"shared_credential_file"`
	Token     string `toml:"token"`

	Namespace string `toml:"namespace"` // CloudWatch Metrics Namespace
	svc       *cloudwatch.CloudWatch
}

var sampleConfig = `
  ## Amazon REGION
  region = "us-east-1"

  ## Amazon Credentials
  ## Credentials are loaded in the following order
  ## 1) Assumed credentials via STS if role_arn is specified
  ## 2) explicit credentials from 'access_key' and 'secret_key'
  ## 3) shared profile from 'profile'
  ## 4) environment variables
  ## 5) shared credentials file
  ## 6) EC2 Instance Profile
  #access_key = ""
  #secret_key = ""
  #token = ""
  #role_arn = ""
  #profile = ""
  #shared_credential_file = ""

  ## Namespace for the CloudWatch MetricDatums
  namespace = "InfluxData/Telegraf"
`

func (c *CloudWatch) SampleConfig() string {
	return sampleConfig
}

func (c *CloudWatch) Description() string {
	return "Configuration for AWS CloudWatch output."
}

func (c *CloudWatch) Connect() error {
	credentialConfig := &internalaws.CredentialConfig{
		Region:    c.Region,
		AccessKey: c.AccessKey,
		SecretKey: c.SecretKey,
		RoleARN:   c.RoleARN,
		Profile:   c.Profile,
		Filename:  c.Filename,
		Token:     c.Token,
	}
	configProvider := credentialConfig.Credentials()
	c.svc = cloudwatch.New(configProvider)
	return nil
}

func (c *CloudWatch) Close() error {
	return nil
}

func (c *CloudWatch) Write(metrics []telegraf.Metric) error {
	for _, m := range metrics {
		err := c.WriteSinglePoint(m)
		if err != nil {
			return err
		}
	}

	return nil
}

// Write data for a single point. A point can have many fields and one field
// is equal to one MetricDatum. There is a limit on how many MetricDatums a
// request can have so we process one Point at a time.
func (c *CloudWatch) WriteSinglePoint(point telegraf.Metric) error {
	datums := BuildMetricDatum(point)

	const maxDatumsPerCall = 20 // PutMetricData only supports up to 20 data metrics per call

	for _, partition := range PartitionDatums(maxDatumsPerCall, datums) {
		err := c.WriteToCloudWatch(partition)

		if err != nil {
			return err
		}
	}

	return nil
}

func (c *CloudWatch) WriteToCloudWatch(datums []*cloudwatch.MetricDatum) error {
	params := &cloudwatch.PutMetricDataInput{
		MetricData: datums,
		Namespace:  aws.String(c.Namespace),
	}

	_, err := c.svc.PutMetricData(params)

	if err != nil {
		log.Printf("E! CloudWatch: Unable to write to CloudWatch : %+v \n", err.Error())
	}

	return err
}

// Partition the MetricDatums into smaller slices of a max size so that are under the limit
// for the AWS API calls.
func PartitionDatums(size int, datums []*cloudwatch.MetricDatum) [][]*cloudwatch.MetricDatum {

	numberOfPartitions := len(datums) / size
	if len(datums)%size != 0 {
		numberOfPartitions += 1
	}

	partitions := make([][]*cloudwatch.MetricDatum, numberOfPartitions)

	for i := 0; i < numberOfPartitions; i++ {
		start := size * i
		end := size * (i + 1)
		if end > len(datums) {
			end = len(datums)
		}

		partitions[i] = datums[start:end]
	}

	return partitions
}

// Make a MetricDatum for each field in a Point. Only fields with values that can be
// converted to float64 are supported. Non-supported fields are skipped.
func BuildMetricDatum(point telegraf.Metric) []*cloudwatch.MetricDatum {
	datums := make([]*cloudwatch.MetricDatum, len(point.Fields()))
	i := 0

	var value float64

	for k, v := range point.Fields() {
		switch t := v.(type) {
		case int:
			value = float64(t)
		case int32:
			value = float64(t)
		case int64:
			value = float64(t)
		case float64:
			value = t
		case bool:
			if t {
				value = 1
			} else {
				value = 0
			}
		case time.Time:
			value = float64(t.Unix())
		default:
			// Skip unsupported type.
			datums = datums[:len(datums)-1]
			continue
		}

		// Do CloudWatch boundary checking
		// Constraints at: http://docs.aws.amazon.com/AmazonCloudWatch/latest/APIReference/API_MetricDatum.html
		if math.IsNaN(value) {
			datums = datums[:len(datums)-1]
			continue
		}
		if math.IsInf(value, 0) {
			datums = datums[:len(datums)-1]
			continue
		}
		if value > 0 && value < float64(8.515920e-109) {
			datums = datums[:len(datums)-1]
			continue
		}
		if value > float64(1.174271e+108) {
			datums = datums[:len(datums)-1]
			continue
		}

		datums[i] = &cloudwatch.MetricDatum{
			MetricName: aws.String(strings.Join([]string{point.Name(), k}, "_")),
			Value:      aws.Float64(value),
			Dimensions: BuildDimensions(point.Tags()),
			Timestamp:  aws.Time(point.Time()),
		}

		i += 1
	}

	return datums
}

// Make a list of Dimensions by using a Point's tags. CloudWatch supports up to
// 10 dimensions per metric so we only keep up to the first 10 alphabetically.
// This always includes the "host" tag if it exists.
func BuildDimensions(mTags map[string]string) []*cloudwatch.Dimension {

	const MaxDimensions = 10
	dimensions := make([]*cloudwatch.Dimension, int(math.Min(float64(len(mTags)), MaxDimensions)))

	i := 0

	// This is pretty ugly but we always want to include the "host" tag if it exists.
	if host, ok := mTags["host"]; ok {
		dimensions[i] = &cloudwatch.Dimension{
			Name:  aws.String("host"),
			Value: aws.String(host),
		}
		i += 1
	}

	var keys []string
	for k := range mTags {
		if k != "host" {
			keys = append(keys, k)
		}
	}
	sort.Strings(keys)

	for _, k := range keys {
		if i >= MaxDimensions {
			break
		}

		dimensions[i] = &cloudwatch.Dimension{
			Name:  aws.String(k),
			Value: aws.String(mTags[k]),
		}

		i += 1
	}

	return dimensions
}

func init() {
	outputs.Add("cloudwatch", func() telegraf.Output {
		return &CloudWatch{}
	})
}
<commit_msg>Reintroduce AWS credential check to cloudwatch output (#3587)<commit_after>package cloudwatch

import (
	"log"
	"math"
	"sort"
	"strings"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/cloudwatch"
	"github.com/aws/aws-sdk-go/service/sts"

	"github.com/influxdata/telegraf"
	internalaws "github.com/influxdata/telegraf/internal/config/aws"
	"github.com/influxdata/telegraf/plugins/outputs"
)

type CloudWatch struct {
	Region    string `toml:"region"`
	AccessKey string `toml:"access_key"`
	SecretKey string `toml:"secret_key"`
	RoleARN   string `toml:"role_arn"`
	Profile   string `toml:"profile"`
	Filename  string `toml:"shared_credential_file"`
	Token     string `toml:"token"`

	Namespace string `toml:"namespace"` // CloudWatch Metrics Namespace
	svc       *cloudwatch.CloudWatch
}

var sampleConfig = `
  ## Amazon REGION
  region = "us-east-1"

  ## Amazon Credentials
  ## Credentials are loaded in the following order
  ## 1) Assumed credentials via STS if role_arn is specified
  ## 2) explicit credentials from 'access_key' and 'secret_key'
  ## 3) shared profile from 'profile'
  ## 4) environment variables
  ## 5) shared credentials file
  ## 6) EC2 Instance Profile
  #access_key = ""
  #secret_key = ""
  #token = ""
  #role_arn = ""
  #profile = ""
  #shared_credential_file = ""

  ## Namespace for the CloudWatch MetricDatums
  namespace = "InfluxData/Telegraf"
`

func (c *CloudWatch) SampleConfig() string {
	return sampleConfig
}

func (c *CloudWatch) Description() string {
	return "Configuration for AWS CloudWatch output."
}

func (c *CloudWatch) Connect() error {
	credentialConfig := &internalaws.CredentialConfig{
		Region:    c.Region,
		AccessKey: c.AccessKey,
		SecretKey: c.SecretKey,
		RoleARN:   c.RoleARN,
		Profile:   c.Profile,
		Filename:  c.Filename,
		Token:     c.Token,
	}
	configProvider := credentialConfig.Credentials()

	stsService := sts.New(configProvider)

	params := &sts.GetCallerIdentityInput{}

	_, err := stsService.GetCallerIdentity(params)

	if err != nil {
		log.Printf("E! cloudwatch: Cannot use credentials to connect to AWS : %+v \n", err.Error())
		return err
	}

	c.svc = cloudwatch.New(configProvider)

	return nil
}

func (c *CloudWatch) Close() error {
	return nil
}

func (c *CloudWatch) Write(metrics []telegraf.Metric) error {
	for _, m := range metrics {
		err := c.WriteSinglePoint(m)
		if err != nil {
			return err
		}
	}

	return nil
}

// Write data for a single point. A point can have many fields and one field
// is equal to one MetricDatum. There is a limit on how many MetricDatums a
// request can have so we process one Point at a time.
func (c *CloudWatch) WriteSinglePoint(point telegraf.Metric) error {
	datums := BuildMetricDatum(point)

	const maxDatumsPerCall = 20 // PutMetricData only supports up to 20 data metrics per call

	for _, partition := range PartitionDatums(maxDatumsPerCall, datums) {
		err := c.WriteToCloudWatch(partition)

		if err != nil {
			return err
		}
	}

	return nil
}

func (c *CloudWatch) WriteToCloudWatch(datums []*cloudwatch.MetricDatum) error {
	params := &cloudwatch.PutMetricDataInput{
		MetricData: datums,
		Namespace:  aws.String(c.Namespace),
	}

	_, err := c.svc.PutMetricData(params)

	if err != nil {
		log.Printf("E! CloudWatch: Unable to write to CloudWatch : %+v \n", err.Error())
	}

	return err
}

// Partition the MetricDatums into smaller slices of a max size so that are under the limit
// for the AWS API calls.
func PartitionDatums(size int, datums []*cloudwatch.MetricDatum) [][]*cloudwatch.MetricDatum {

	numberOfPartitions := len(datums) / size
	if len(datums)%size != 0 {
		numberOfPartitions += 1
	}

	partitions := make([][]*cloudwatch.MetricDatum, numberOfPartitions)

	for i := 0; i < numberOfPartitions; i++ {
		start := size * i
		end := size * (i + 1)
		if end > len(datums) {
			end = len(datums)
		}

		partitions[i] = datums[start:end]
	}

	return partitions
}

// Make a MetricDatum for each field in a Point. Only fields with values that can be
// converted to float64 are supported. Non-supported fields are skipped.
func BuildMetricDatum(point telegraf.Metric) []*cloudwatch.MetricDatum {
	datums := make([]*cloudwatch.MetricDatum, len(point.Fields()))
	i := 0

	var value float64

	for k, v := range point.Fields() {
		switch t := v.(type) {
		case int:
			value = float64(t)
		case int32:
			value = float64(t)
		case int64:
			value = float64(t)
		case float64:
			value = t
		case bool:
			if t {
				value = 1
			} else {
				value = 0
			}
		case time.Time:
			value = float64(t.Unix())
		default:
			// Skip unsupported type.
			datums = datums[:len(datums)-1]
			continue
		}

		// Do CloudWatch boundary checking
		// Constraints at: http://docs.aws.amazon.com/AmazonCloudWatch/latest/APIReference/API_MetricDatum.html
		if math.IsNaN(value) {
			datums = datums[:len(datums)-1]
			continue
		}
		if math.IsInf(value, 0) {
			datums = datums[:len(datums)-1]
			continue
		}
		if value > 0 && value < float64(8.515920e-109) {
			datums = datums[:len(datums)-1]
			continue
		}
		if value > float64(1.174271e+108) {
			datums = datums[:len(datums)-1]
			continue
		}

		datums[i] = &cloudwatch.MetricDatum{
			MetricName: aws.String(strings.Join([]string{point.Name(), k}, "_")),
			Value:      aws.Float64(value),
			Dimensions: BuildDimensions(point.Tags()),
			Timestamp:  aws.Time(point.Time()),
		}

		i += 1
	}

	return datums
}

// Make a list of Dimensions by using a Point's tags. CloudWatch supports up to
// 10 dimensions per metric so we only keep up to the first 10 alphabetically.
// This always includes the "host" tag if it exists.
func BuildDimensions(mTags map[string]string) []*cloudwatch.Dimension {

	const MaxDimensions = 10
	dimensions := make([]*cloudwatch.Dimension, int(math.Min(float64(len(mTags)), MaxDimensions)))

	i := 0

	// This is pretty ugly but we always want to include the "host" tag if it exists.
	if host, ok := mTags["host"]; ok {
		dimensions[i] = &cloudwatch.Dimension{
			Name:  aws.String("host"),
			Value: aws.String(host),
		}
		i += 1
	}

	var keys []string
	for k := range mTags {
		if k != "host" {
			keys = append(keys, k)
		}
	}
	sort.Strings(keys)

	for _, k := range keys {
		if i >= MaxDimensions {
			break
		}

		dimensions[i] = &cloudwatch.Dimension{
			Name:  aws.String(k),
			Value: aws.String(mTags[k]),
		}

		i += 1
	}

	return dimensions
}

func init() {
	outputs.Add("cloudwatch", func() telegraf.Output {
		return &CloudWatch{}
	})
}
<|endoftext|>


<commit_before>package announcer

import (
	"context"
	"net"
	"time"

	"github.com/cenkalti/backoff"
	"github.com/cenkalti/rain/internal/logger"
	"github.com/cenkalti/rain/torrent/internal/tracker"
)

const stopEventTimeout = time.Minute

type Announcer struct {
	url          string
	log          logger.Logger
	completedC   chan struct{}
	newPeers     chan []*net.TCPAddr
	tracker      tracker.Tracker
	backoff      backoff.BackOff
	nextAnnounce time.Duration
	requests     chan *Request
	closeC       chan struct{}
	doneC        chan struct{}
}

type Request struct {
	Response chan Response
}

type Response struct {
	Transfer tracker.Transfer
}

func New(trk tracker.Tracker, requests chan *Request, completedC chan struct{}, newPeers chan []*net.TCPAddr, l logger.Logger) *Announcer {
	return &Announcer{
		tracker:    trk,
		log:        l,
		completedC: completedC,
		newPeers:   newPeers,
		requests:   requests,
		closeC:     make(chan struct{}),
		doneC:      make(chan struct{}),
		backoff: &backoff.ExponentialBackOff{
			InitialInterval:     5 * time.Second,
			RandomizationFactor: 0.5,
			Multiplier:          2,
			MaxInterval:         30 * time.Minute,
			MaxElapsedTime:      0, // never stop
			Clock:               backoff.SystemClock,
		},
	}
}

func (a *Announcer) Close() {
	close(a.closeC)
	<-a.doneC
}

func (a *Announcer) Run() {
	defer close(a.doneC)

	ctx, cancel := context.WithCancel(context.Background())
	go func() {
		<-a.closeC
		cancel()
	}()

	a.backoff.Reset()
	a.announce(ctx, tracker.EventStarted)
	for {
		select {
		case <-time.After(a.nextAnnounce):
			a.announce(ctx, tracker.EventNone)
		case <-a.completedC:
			a.announce(ctx, tracker.EventCompleted)
			a.completedC = nil
		case <-a.closeC:
			go a.announceStopAndClose()
			return
		}
	}
}

func (a *Announcer) announce(ctx context.Context, e tracker.Event) {
	req := &Request{
		Response: make(chan Response),
	}
	select {
	case a.requests <- req:
	case <-ctx.Done():
		return
	}
	var resp Response
	select {
	case resp = <-req.Response:
	case <-ctx.Done():
		return
	}
	r, err := a.tracker.Announce(ctx, resp.Transfer, e)
	if err == context.Canceled {
		return
	}
	if err != nil {
		if _, ok := err.(*net.OpError); ok {
			a.log.Debugln("net operation error:", err)
		} else {
			a.log.Errorln("announce error:", err)
		}
		a.nextAnnounce = a.backoff.NextBackOff()
	} else {
		a.backoff.Reset()
		a.nextAnnounce = r.Interval
		select {
		case a.newPeers <- r.Peers:
		case <-ctx.Done():
		}
	}
}

func (a *Announcer) announceStopAndClose() {
	ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(stopEventTimeout))
	defer cancel()
	a.announce(ctx, tracker.EventStopped)
	a.tracker.Close()
}
<commit_msg>refactor<commit_after>package announcer

import (
	"context"
	"net"
	"time"

	"github.com/cenkalti/backoff"
	"github.com/cenkalti/rain/internal/logger"
	"github.com/cenkalti/rain/torrent/internal/tracker"
)

const stopEventTimeout = time.Minute

type Announcer struct {
	url          string
	log          logger.Logger
	completedC   chan struct{}
	newPeers     chan []*net.TCPAddr
	tracker      tracker.Tracker
	backoff      backoff.BackOff
	nextAnnounce time.Duration
	requests     chan *Request
	closeC       chan struct{}
	doneC        chan struct{}
}

type Request struct {
	Response chan Response
}

type Response struct {
	Transfer tracker.Transfer
}

func New(trk tracker.Tracker, requests chan *Request, completedC chan struct{}, newPeers chan []*net.TCPAddr, l logger.Logger) *Announcer {
	return &Announcer{
		tracker:    trk,
		log:        l,
		completedC: completedC,
		newPeers:   newPeers,
		requests:   requests,
		closeC:     make(chan struct{}),
		doneC:      make(chan struct{}),
		backoff: &backoff.ExponentialBackOff{
			InitialInterval:     5 * time.Second,
			RandomizationFactor: 0.5,
			Multiplier:          2,
			MaxInterval:         30 * time.Minute,
			MaxElapsedTime:      0, // never stop
			Clock:               backoff.SystemClock,
		},
	}
}

func (a *Announcer) Close() {
	close(a.closeC)
	<-a.doneC
}

func (a *Announcer) Run() {
	defer close(a.doneC)

	ctx, cancel := context.WithCancel(context.Background())
	go func() {
		<-a.closeC
		cancel()
	}()

	a.backoff.Reset()
	a.announce(ctx, tracker.EventStarted)
	for {
		select {
		case <-time.After(a.nextAnnounce):
			a.announce(ctx, tracker.EventNone)
		case <-a.completedC:
			a.announce(ctx, tracker.EventCompleted)
			a.completedC = nil
		case <-a.closeC:
			go a.announceStopAndClose()
			return
		}
	}
}

func (a *Announcer) announce(ctx context.Context, e tracker.Event) {
	req := &Request{
		Response: make(chan Response),
	}
	select {
	case a.requests <- req:
	case <-ctx.Done():
		return
	}
	var resp Response
	select {
	case resp = <-req.Response:
	case <-ctx.Done():
		return
	}
	r, err := a.tracker.Announce(ctx, resp.Transfer, e)
	if err == context.Canceled {
		return
	}
	if err != nil {
		if _, ok := err.(*net.OpError); ok {
			a.log.Debugln("net operation error:", err)
		} else {
			a.log.Errorln("announce error:", err)
		}
		a.nextAnnounce = a.backoff.NextBackOff()
		return
	}
	a.backoff.Reset()
	a.nextAnnounce = r.Interval
	select {
	case a.newPeers <- r.Peers:
	case <-ctx.Done():
	}
}

func (a *Announcer) announceStopAndClose() {
	ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(stopEventTimeout))
	defer cancel()
	a.announce(ctx, tracker.EventStopped)
	a.tracker.Close()
}
<|endoftext|>


<commit_before>package termite

import (
	"log"
	"os"
	"sync"
)

var _ = log.Println

type fileSetWaiter struct {
	process func(fset FileSet) os.Error
	master *Master
	mirror *mirrorConnection
	sync.Mutex
	channels map[int]chan int
}

func newFileSetWaiter(proc func(FileSet) os.Error) *fileSetWaiter {
	return &fileSetWaiter{
		process:  proc,
		channels: make(map[int]chan int),
	}
}

func (me *fileSetWaiter) newChannel(id int) chan int {
	me.Lock()
	defer me.Unlock()

	c := make(chan int, 1)
	me.channels[id] = c
	return c
}

func (me *fileSetWaiter) findChannel(id int) chan int {
	me.Lock()
	defer me.Unlock()
	return me.channels[id]
}

func (me *fileSetWaiter) signal(id int) {
	me.Lock()
	defer me.Unlock()
	ch := me.channels[id]
	if ch != nil {
		ch <- 1
		close(ch)
		me.channels[id] = nil, false
	}
}

func (me *fileSetWaiter) flush(id int) {
	me.Lock()
	defer me.Unlock()
	ch := me.channels[id]
	close(ch)
	me.channels[id] = nil, false
}

func (me *fileSetWaiter) drop(id int) {
	me.Lock()
	defer me.Unlock()
	me.channels[id] = nil, false
}

func (me *fileSetWaiter) wait(rep *WorkResponse, waitId int) (err os.Error) {
	log.Println("Got data for tasks: ", rep.TaskIds)

	if rep.FileSet != nil {
		err = me.process(*rep.FileSet)
		for _, id := range rep.TaskIds {
			if id == waitId {
				continue
			}
			if err == nil {
				me.signal(id)
			} else {
				me.flush(id)
			}
		}
	} else {
		completion := me.findChannel(waitId)
		if completion != nil {
			// completion may be nil if the response
			// already came in.
			_, ok := <-completion
			if !ok {
				return os.NewError("files were never sent.")
			}
		}
	}
	me.drop(waitId)
	return err
}
<commit_msg>Drop unused members in fileSetWaiter.<commit_after>package termite

import (
	"log"
	"os"
	"sync"
)

var _ = log.Println

type fileSetWaiter struct {
	process func(fset FileSet) os.Error
	sync.Mutex
	channels map[int]chan int
}

func newFileSetWaiter(proc func(FileSet) os.Error) *fileSetWaiter {
	return &fileSetWaiter{
		process:  proc,
		channels: make(map[int]chan int),
	}
}

func (me *fileSetWaiter) newChannel(id int) chan int {
	me.Lock()
	defer me.Unlock()

	c := make(chan int, 1)
	me.channels[id] = c
	return c
}

func (me *fileSetWaiter) findChannel(id int) chan int {
	me.Lock()
	defer me.Unlock()
	return me.channels[id]
}

func (me *fileSetWaiter) signal(id int) {
	me.Lock()
	defer me.Unlock()
	ch := me.channels[id]
	if ch != nil {
		ch <- 1
		close(ch)
		me.channels[id] = nil, false
	}
}

func (me *fileSetWaiter) flush(id int) {
	me.Lock()
	defer me.Unlock()
	ch := me.channels[id]
	close(ch)
	me.channels[id] = nil, false
}

func (me *fileSetWaiter) drop(id int) {
	me.Lock()
	defer me.Unlock()
	me.channels[id] = nil, false
}

func (me *fileSetWaiter) wait(rep *WorkResponse, waitId int) (err os.Error) {
	log.Println("Got data for tasks: ", rep.TaskIds)

	if rep.FileSet != nil {
		err = me.process(*rep.FileSet)
		for _, id := range rep.TaskIds {
			if id == waitId {
				continue
			}
			if err == nil {
				me.signal(id)
			} else {
				me.flush(id)
			}
		}
	} else {
		completion := me.findChannel(waitId)
		if completion != nil {
			// completion may be nil if the response
			// already came in.
			_, ok := <-completion
			if !ok {
				return os.NewError("files were never sent.")
			}
		}
	}
	me.drop(waitId)
	return err
}
<|endoftext|>


<commit_before>package rewrite

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"strings"

	"github.com/codelingo/codelingo/flows/codelingo/rewrite/rewrite/option"
	rewriterpc "github.com/codelingo/codelingo/flows/codelingo/rewrite/rpc"
	flowutil "github.com/codelingo/codelingo/sdk/flow"
	"github.com/urfave/cli"

	"github.com/juju/errors"
)

func Write(results []*flowutil.DecoratedResult) error {

	// TODO(waigani) use one open file handler per file to write all changes
	// and use a buffered writer: https://www.devdungeon.com/content/working-
	// files-go#write_buffered

	// first group all results by file
	resultMap := make(map[string][]*flowutil.DecoratedResult)

	for _, result := range results {
		filename := result.Payload.(*rewriterpc.Hunk).Filename
		resultMap[filename] = append(resultMap[filename], result)
	}

	seenNewFile := make(map[string]bool)

	for filename, results := range resultMap {

		rootPath, err := flowutil.GitCMD("rev-parse", "--show-toplevel")
		if err != nil {
			return errors.Trace(err)
		}

		fullPath := filepath.Join(strings.TrimSuffix(rootPath, "\n"), filename)
		fileSRC, err := ioutil.ReadFile(fullPath)
		if err != nil {
			return errors.Trace(err)
		}

		// then order results by start offset such that we apply the
		// modifications to the file from the bottom up.
		sort.Sort(byOffset(results))
		var i int
		var result *flowutil.DecoratedResult
		for i, result = range results {

			ctx := result.Ctx
			hunk := result.Payload.(*rewriterpc.Hunk)

			if ctx.IsSet("new-file") {

				newFileName := ctx.String("new-file")
				if seenNewFile[newFileName] {
					if err != nil {
						return errors.Errorf("cannot add new file %q more than once", newFileName)
					}
				}

				perm := 0755
				if ctx.IsSet("new-file-perm") {
					perm = ctx.Int("new-file-perm")
				}
				if err := ioutil.WriteFile(filepath.Join(filepath.Dir(fullPath), newFileName), []byte(hunk.SRC), os.FileMode(perm)); err != nil {
					return errors.Trace(err)
				}

				seenNewFile[newFileName] = true
				continue
			}

			fileSRC, _, err = newFileSRC(ctx, hunk, fileSRC)
			if err != nil {
				return errors.Trace(err)
			}

		}

		if err := ioutil.WriteFile(fullPath, []byte(fileSRC), 0644); err != nil {
			return errors.Trace(err)
		}
		fmt.Printf("%d modifications made to file %s\n", i+1, fullPath)

	}

	return nil
}

// return start and end of the line containing the given offset
func lineOffsets(src []byte, offset int32) []int32 {
	var start, end int32
	// find start
	for i := offset; i >= 0; i-- {
		if src[i] == '\n' {
			break
		}
		start = i
	}

	// find end
	for i := offset; i < int32(len(src)); i++ {
		if src[i] == '\n' {
			break
		}
		end = i
	}
	return []int32{start, end}
}

type partitionedFile struct {
	srcBeforeStartOffset func() []byte
	srcAfterStartOffset  func() []byte
	srcBeforeEndOffset   func() []byte
	srcAfterEndOffset    func() []byte

	srcBeforeStartLine func() []byte
	srcAfterStartLine  func() []byte
	srcBeforeEndLine   func() []byte
	srcAfterEndLine    func() []byte

	startLineOffsets func() []int32
	endLineOffsets   func() []int32
}

func splitSRC(hunk *rewriterpc.Hunk, fileSRC []byte) partitionedFile {
	startLineOffsets := lineOffsets(fileSRC, hunk.StartOffset)
	endLineOffsets := lineOffsets(fileSRC, hunk.EndOffset)

	return partitionedFile{
		srcBeforeStartOffset: func() []byte { return []byte(string(fileSRC))[0:hunk.StartOffset] },
		srcAfterStartOffset:  func() []byte { return []byte(string(fileSRC))[hunk.StartOffset+1:] },
		srcBeforeEndOffset:   func() []byte { return []byte(string(fileSRC))[0 : hunk.EndOffset-1] },
		srcAfterEndOffset:    func() []byte { return []byte(string(fileSRC))[hunk.EndOffset:] },

		srcBeforeStartLine: func() []byte { return []byte(string(fileSRC))[0:startLineOffsets[0]] },
		srcAfterStartLine:  func() []byte { return []byte(string(fileSRC))[startLineOffsets[1]+1:] },
		srcBeforeEndLine:   func() []byte { return []byte(string(fileSRC))[0:endLineOffsets[0]] },
		srcAfterEndLine:    func() []byte { return []byte(string(fileSRC))[endLineOffsets[1]+1:] },

		startLineOffsets: func() []int32 { return startLineOffsets },
		endLineOffsets:   func() []int32 { return endLineOffsets },
	}
}

type comment struct {
	content string
	// TODO: comments should span multiple lines, but github doesn't allow that https://github.community/t5/How-to-use-Git-and-GitHub/Feature-request-Multiline-reviews-in-pull-requests/m-p/9850#M3225
	line int
}

func newFileSRC(ctx *cli.Context, hunk *rewriterpc.Hunk, fileSRC []byte) ([]byte, *comment, error) {
	parts := splitSRC(hunk, fileSRC)

	rewrittenFile, err := rewriteFile(ctx, fileSRC, []byte(hunk.SRC), parts, hunk)
	if err != nil {
		return nil, nil, errors.Trace(err)
	}

	var c *comment
	if hunk.Comment != "" {
		commentedSRC, err := rewriteFile(ctx, fileSRC, []byte(hunk.Comment), parts, hunk)
		if err != nil {
			return nil, nil, errors.Trace(err)
		}

		// Find updated line in new rewrittenFile
		commentedLines := strings.Split(string(commentedSRC), "\n")
		for lineNumber, updatedLine := range strings.Split(string(rewrittenFile), "\n") {
			if len(commentedSRC) <= lineNumber {
				return nil, nil, errors.New("reached end of commented file before finding updated line")
			}

			commentedLine := commentedLines[lineNumber]
			if updatedLine != commentedLine {
				c = &comment{
					content: string(commentedLine),
					line:    lineNumber,
				}
				break
			}
		}
	}
	return rewrittenFile, c, nil
}

func rewriteFile(ctx *cli.Context, inputSRC, newSRC []byte, parts partitionedFile, hunk *rewriterpc.Hunk) ([]byte, error) {
	fileSRC := []byte(string(inputSRC))

	opts, err := option.New(ctx)
	if err != nil {
		return nil, errors.Trace(err)
	}
	newLine := append(newSRC, '\n')
	newLineAfter := append([]byte{'\n'}, newSRC...)

	switch {
	case opts.IsReplace() && opts.IsStartToEndOffset() && opts.IsByte():
		// replace between start and end bytes
		fileSRC = append(parts.srcBeforeStartOffset(), append(newSRC, parts.srcAfterEndOffset()...)...)

	case opts.IsReplace() && opts.IsStartOffset() && opts.IsByte():
		// replace only the start byte
		fileSRC = append(parts.srcBeforeStartOffset(), append(newSRC, parts.srcAfterStartOffset()...)...)

	case opts.IsReplace() && opts.IsEndOffset() && opts.IsByte():
		// replace only the end byte
		fileSRC = append(parts.srcBeforeEndOffset(), append(newSRC, parts.srcAfterEndOffset()...)...)

	case opts.IsReplace() && opts.IsStartToEndOffset() && opts.IsLine():
		// o.Do(func() {
		fileSRC = append(parts.srcBeforeStartLine(), append(newSRC, parts.srcAfterEndLine()...)...)
		// })
	case opts.IsReplace() && opts.IsStartOffset() && opts.IsLine():
		fileSRC = append(parts.srcBeforeStartLine(), append(newSRC, parts.srcAfterStartLine()...)...)

	case opts.IsReplace() && opts.IsEndOffset() && opts.IsLine():
		// replace whole line
		fileSRC = append(parts.srcBeforeEndLine(), append(newSRC, parts.srcAfterEndLine()...)...)

	case opts.IsPrepend() && opts.IsStartToEndOffset() && opts.IsByte():
		fallthrough
	case opts.IsPrepend() && opts.IsStartOffset() && opts.IsByte():
		// insert before startoffset
		// TODO: remove reference to hunk
		fileSRC = append(parts.srcBeforeStartOffset(), append(newSRC, fileSRC[hunk.StartOffset:]...)...)
	case opts.IsPrepend() && opts.IsEndOffset() && opts.IsByte():
		// insert before endoffset
		fileSRC = append(parts.srcBeforeEndOffset(), append(newSRC, fileSRC[hunk.EndOffset-1:]...)...)

	case opts.IsPrepend() && opts.IsStartToEndOffset() && opts.IsLine():
		fallthrough
	case opts.IsPrepend() && opts.IsStartOffset() && opts.IsLine():
		// insert on new line above startoffset
		fileSRC = append(parts.srcBeforeStartLine(), append(newLine, fileSRC[parts.startLineOffsets()[0]:]...)...)

	case opts.IsPrepend() && opts.IsEndOffset() && opts.IsLine():
		// insert on new line above endoffset
		fileSRC = append(parts.srcBeforeEndLine(), append(newLine, fileSRC[parts.endLineOffsets()[0]:]...)...)

	case opts.IsAppend() && opts.IsStartToEndOffset() && opts.IsByte():
		fallthrough
	case opts.IsAppend() && opts.IsEndOffset() && opts.IsByte():
		// insert after endoffset
		fileSRC = append(fileSRC[0:hunk.EndOffset], append(newSRC, parts.srcAfterEndOffset()...)...)

	case opts.IsAppend() && opts.IsStartOffset() && opts.IsByte():
		// insert after startoffset
		fileSRC = append(fileSRC[0:hunk.StartOffset+1], append(newSRC, parts.srcAfterStartOffset()...)...)

	case opts.IsAppend() && opts.IsStartToEndOffset() && opts.IsLine():
		fallthrough
	case opts.IsAppend() && opts.IsEndOffset() && opts.IsLine():
		// insert on new line after endoffset
		fileSRC = append(fileSRC[0:parts.endLineOffsets()[1]+1], append(newLineAfter, parts.srcAfterEndLine()...)...)

	case opts.IsAppend() && opts.IsStartOffset() && opts.IsLine():
		// insert on new line after startoffset
		fileSRC = append(fileSRC[0:parts.startLineOffsets()[1]+1], append(newLineAfter, parts.srcAfterStartLine()...)...)
	}
	return fileSRC, nil
}

type byOffset []*flowutil.DecoratedResult

func (o byOffset) Len() int {
	return len(o)
}

func (o byOffset) Swap(i, j int) {
	o[i], o[j] = o[j], o[i]
}

func (o byOffset) Less(i, j int) bool {
	return o[j].Payload.(*rewriterpc.Hunk).StartOffset < o[i].Payload.(*rewriterpc.Hunk).StartOffset
}
<commit_msg>Output to JSON.<commit_after>package rewrite

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

	"github.com/codelingo/codelingo/flows/codelingo/rewrite/rewrite/option"
	rewriterpc "github.com/codelingo/codelingo/flows/codelingo/rewrite/rpc"
	flowutil "github.com/codelingo/codelingo/sdk/flow"
	"github.com/urfave/cli"

	"github.com/juju/errors"
)

func Write(results []*flowutil.DecoratedResult) error {

	// TODO(waigani) use one open file handler per file to write all changes
	// and use a buffered writer: https://www.devdungeon.com/content/working-
	// files-go#write_buffered

	// first group all results by file
	resultMap := make(map[string][]*flowutil.DecoratedResult)

	for _, result := range results {
		filename := result.Payload.(*rewriterpc.Hunk).Filename
		resultMap[filename] = append(resultMap[filename], result)
	}

	seenNewFile := make(map[string]bool)
	var comments []*comment

	for filename, results := range resultMap {

		rootPath, err := flowutil.GitCMD("rev-parse", "--show-toplevel")
		if err != nil {
			return errors.Trace(err)
		}

		fullPath := filepath.Join(strings.TrimSuffix(rootPath, "\n"), filename)
		fileSRC, err := ioutil.ReadFile(fullPath)
		if err != nil {
			return errors.Trace(err)
		}

		// then order results by start offset such that we apply the
		// modifications to the file from the bottom up.
		sort.Sort(byOffset(results))
		var i int
		var result *flowutil.DecoratedResult
		for i, result = range results {

			ctx := result.Ctx
			hunk := result.Payload.(*rewriterpc.Hunk)

			if ctx.IsSet("new-file") {

				newFileName := ctx.String("new-file")
				if seenNewFile[newFileName] {
					if err != nil {
						return errors.Errorf("cannot add new file %q more than once", newFileName)
					}
				}

				perm := 0755
				if ctx.IsSet("new-file-perm") {
					perm = ctx.Int("new-file-perm")
				}
				if err := ioutil.WriteFile(filepath.Join(filepath.Dir(fullPath), newFileName), []byte(hunk.SRC), os.FileMode(perm)); err != nil {
					return errors.Trace(err)
				}

				seenNewFile[newFileName] = true
				continue
			}

			var comment *comment
			fileSRC, comment, err = newFileSRC(ctx, hunk, fileSRC)
			if err != nil {
				return errors.Trace(err)
			}

			comment.Path = fullPath
			comments = append(comments, comment)
		}

		if err := ioutil.WriteFile(fullPath, []byte(fileSRC), 0644); err != nil {
			return errors.Trace(err)
		}
		fmt.Printf("%d modifications made to file %s\n", i+1, fullPath)

	}

	if len(comments) > 0 {
		output, err := json.Marshal(comments)
		if err != nil {
			return errors.Trace(err)
		}

		// TODO: read filepath from flag
		err = ioutil.WriteFile("~/.codelingo/latestcomments", output, 0644)
		if err != nil {
			return errors.Trace(err)
		}
	}

	return nil
}

// return start and end of the line containing the given offset
func lineOffsets(src []byte, offset int32) []int32 {
	var start, end int32
	// find start
	for i := offset; i >= 0; i-- {
		if src[i] == '\n' {
			break
		}
		start = i
	}

	// find end
	for i := offset; i < int32(len(src)); i++ {
		if src[i] == '\n' {
			break
		}
		end = i
	}
	return []int32{start, end}
}

type partitionedFile struct {
	srcBeforeStartOffset func() []byte
	srcAfterStartOffset  func() []byte
	srcBeforeEndOffset   func() []byte
	srcAfterEndOffset    func() []byte

	srcBeforeStartLine func() []byte
	srcAfterStartLine  func() []byte
	srcBeforeEndLine   func() []byte
	srcAfterEndLine    func() []byte

	startLineOffsets func() []int32
	endLineOffsets   func() []int32
}

func splitSRC(hunk *rewriterpc.Hunk, fileSRC []byte) partitionedFile {
	startLineOffsets := lineOffsets(fileSRC, hunk.StartOffset)
	endLineOffsets := lineOffsets(fileSRC, hunk.EndOffset)

	return partitionedFile{
		srcBeforeStartOffset: func() []byte { return []byte(string(fileSRC))[0:hunk.StartOffset] },
		srcAfterStartOffset:  func() []byte { return []byte(string(fileSRC))[hunk.StartOffset+1:] },
		srcBeforeEndOffset:   func() []byte { return []byte(string(fileSRC))[0 : hunk.EndOffset-1] },
		srcAfterEndOffset:    func() []byte { return []byte(string(fileSRC))[hunk.EndOffset:] },

		srcBeforeStartLine: func() []byte { return []byte(string(fileSRC))[0:startLineOffsets[0]] },
		srcAfterStartLine:  func() []byte { return []byte(string(fileSRC))[startLineOffsets[1]+1:] },
		srcBeforeEndLine:   func() []byte { return []byte(string(fileSRC))[0:endLineOffsets[0]] },
		srcAfterEndLine:    func() []byte { return []byte(string(fileSRC))[endLineOffsets[1]+1:] },

		startLineOffsets: func() []int32 { return startLineOffsets },
		endLineOffsets:   func() []int32 { return endLineOffsets },
	}
}

type comment struct {
	Content string `json:"content"`
	// TODO: comments should span multiple lines, but github doesn't allow that https://github.community/t5/How-to-use-Git-and-GitHub/Feature-request-Multiline-reviews-in-pull-requests/m-p/9850#M3225
	Line int    `json:"line"`
	Path string `json:"path"`
}

func newFileSRC(ctx *cli.Context, hunk *rewriterpc.Hunk, fileSRC []byte) ([]byte, *comment, error) {
	parts := splitSRC(hunk, fileSRC)

	rewrittenFile, err := rewriteFile(ctx, fileSRC, []byte(hunk.SRC), parts, hunk)
	if err != nil {
		return nil, nil, errors.Trace(err)
	}

	var c *comment
	if hunk.Comment != "" {
		commentedSRC, err := rewriteFile(ctx, fileSRC, []byte(hunk.Comment), parts, hunk)
		if err != nil {
			return nil, nil, errors.Trace(err)
		}

		// Find updated line in new rewrittenFile
		commentedLines := strings.Split(string(commentedSRC), "\n")
		for lineNumber, updatedLine := range strings.Split(string(rewrittenFile), "\n") {
			if len(commentedSRC) <= lineNumber {
				return nil, nil, errors.New("reached end of commented file before finding updated line")
			}

			commentedLine := commentedLines[lineNumber]
			if updatedLine != commentedLine {
				c = &comment{
					Content: string(commentedLine),
					Line:    lineNumber,
				}
				break
			}
		}
	}
	return rewrittenFile, c, nil
}

func rewriteFile(ctx *cli.Context, inputSRC, newSRC []byte, parts partitionedFile, hunk *rewriterpc.Hunk) ([]byte, error) {
	fileSRC := []byte(string(inputSRC))

	opts, err := option.New(ctx)
	if err != nil {
		return nil, errors.Trace(err)
	}
	newLine := append(newSRC, '\n')
	newLineAfter := append([]byte{'\n'}, newSRC...)

	switch {
	case opts.IsReplace() && opts.IsStartToEndOffset() && opts.IsByte():
		// replace between start and end bytes
		fileSRC = append(parts.srcBeforeStartOffset(), append(newSRC, parts.srcAfterEndOffset()...)...)

	case opts.IsReplace() && opts.IsStartOffset() && opts.IsByte():
		// replace only the start byte
		fileSRC = append(parts.srcBeforeStartOffset(), append(newSRC, parts.srcAfterStartOffset()...)...)

	case opts.IsReplace() && opts.IsEndOffset() && opts.IsByte():
		// replace only the end byte
		fileSRC = append(parts.srcBeforeEndOffset(), append(newSRC, parts.srcAfterEndOffset()...)...)

	case opts.IsReplace() && opts.IsStartToEndOffset() && opts.IsLine():
		// o.Do(func() {
		fileSRC = append(parts.srcBeforeStartLine(), append(newSRC, parts.srcAfterEndLine()...)...)
		// })
	case opts.IsReplace() && opts.IsStartOffset() && opts.IsLine():
		fileSRC = append(parts.srcBeforeStartLine(), append(newSRC, parts.srcAfterStartLine()...)...)

	case opts.IsReplace() && opts.IsEndOffset() && opts.IsLine():
		// replace whole line
		fileSRC = append(parts.srcBeforeEndLine(), append(newSRC, parts.srcAfterEndLine()...)...)

	case opts.IsPrepend() && opts.IsStartToEndOffset() && opts.IsByte():
		fallthrough
	case opts.IsPrepend() && opts.IsStartOffset() && opts.IsByte():
		// insert before startoffset
		// TODO: remove reference to hunk
		fileSRC = append(parts.srcBeforeStartOffset(), append(newSRC, fileSRC[hunk.StartOffset:]...)...)
	case opts.IsPrepend() && opts.IsEndOffset() && opts.IsByte():
		// insert before endoffset
		fileSRC = append(parts.srcBeforeEndOffset(), append(newSRC, fileSRC[hunk.EndOffset-1:]...)...)

	case opts.IsPrepend() && opts.IsStartToEndOffset() && opts.IsLine():
		fallthrough
	case opts.IsPrepend() && opts.IsStartOffset() && opts.IsLine():
		// insert on new line above startoffset
		fileSRC = append(parts.srcBeforeStartLine(), append(newLine, fileSRC[parts.startLineOffsets()[0]:]...)...)

	case opts.IsPrepend() && opts.IsEndOffset() && opts.IsLine():
		// insert on new line above endoffset
		fileSRC = append(parts.srcBeforeEndLine(), append(newLine, fileSRC[parts.endLineOffsets()[0]:]...)...)

	case opts.IsAppend() && opts.IsStartToEndOffset() && opts.IsByte():
		fallthrough
	case opts.IsAppend() && opts.IsEndOffset() && opts.IsByte():
		// insert after endoffset
		fileSRC = append(fileSRC[0:hunk.EndOffset], append(newSRC, parts.srcAfterEndOffset()...)...)

	case opts.IsAppend() && opts.IsStartOffset() && opts.IsByte():
		// insert after startoffset
		fileSRC = append(fileSRC[0:hunk.StartOffset+1], append(newSRC, parts.srcAfterStartOffset()...)...)

	case opts.IsAppend() && opts.IsStartToEndOffset() && opts.IsLine():
		fallthrough
	case opts.IsAppend() && opts.IsEndOffset() && opts.IsLine():
		// insert on new line after endoffset
		fileSRC = append(fileSRC[0:parts.endLineOffsets()[1]+1], append(newLineAfter, parts.srcAfterEndLine()...)...)

	case opts.IsAppend() && opts.IsStartOffset() && opts.IsLine():
		// insert on new line after startoffset
		fileSRC = append(fileSRC[0:parts.startLineOffsets()[1]+1], append(newLineAfter, parts.srcAfterStartLine()...)...)
	}
	return fileSRC, nil
}

type byOffset []*flowutil.DecoratedResult

func (o byOffset) Len() int {
	return len(o)
}

func (o byOffset) Swap(i, j int) {
	o[i], o[j] = o[j], o[i]
}

func (o byOffset) Less(i, j int) bool {
	return o[j].Payload.(*rewriterpc.Hunk).StartOffset < o[i].Payload.(*rewriterpc.Hunk).StartOffset
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"log"
	"math/rand"
	"net/http"
	"sort"
	"strconv"
	"sync"
	"time"
)

type sid string
type pid string

type Server struct {
	sid     sid
	players map[pid]*Player
}

type Player struct {
	pid          pid
	sid          sid
	name         string
	color        uint32
	lastUpdate   time.Time
	lastX, lastY float64
	lastScore    int32
}

var mu sync.Mutex
var rnd = rand.New(rand.NewSource(time.Now().Unix()))
var players = map[pid]*Player{}
var servers = map[sid]*Server{}

func newId() string {
	return strconv.FormatInt(rnd.Int63(), 16)
}

type GetIdReq struct {
	Sid   string  `json:"sid"`
	Name  string  `json:"name"`
	Color uint32  `json:"color"`
	X     float64 `json:"x"`
	Y     float64 `json:"y"`
	Score int32   `json:"score"`
}

type GetIdRsp struct {
	Id string `json:"id"`
}

func getId(w http.ResponseWriter, r *http.Request) {
	log.Println(r.URL)
	if SetCorsHeaders(w.Header(), r) {
		return
	}

	req := GetIdReq{}
	if !ReadJson(w, r, &req) {
		return
	}

	if len(req.Sid) == 0 {
		http.Error(w, "missing sId", http.StatusBadRequest)
		return
	}
	if len(req.Name) == 0 {
		http.Error(w, "missing name", http.StatusBadRequest)
		return
	}

	mu.Lock()
	defer mu.Unlock()

	sid := sid(req.Sid)
	id := pid(newId())
	server, ok := servers[sid]
	if !ok {
		server = &Server{
			sid:     sid,
			players: map[pid]*Player{},
		}
		servers[sid] = server
	}

	player := &Player{
		pid:        id,
		sid:        sid,
		name:       req.Name,
		color:      req.Color,
		lastX:      req.X,
		lastY:      req.Y,
		lastScore:  req.Score,
		lastUpdate: time.Now(),
	}
	players[id] = player
	server.players[id] = player

	JsonRespond(w, &GetIdRsp{
		Id: string(player.pid),
	})
}

type UpdateReq struct {
	Id    string  `json:"id"`
	X     float64 `json:"x"`
	Y     float64 `json:"y"`
	Score int32   `json:"score"`
}

type PlayerStatus struct {
	Name  string  `json:"name"`
	Color uint32  `json:"color"`
	X     float64 `json:"x"`
	Y     float64 `json:"y"`
	Score int32   `json:"score"`
	IsMe  bool    `json:"is_me"`
}

type UpdateRsp struct {
	Players []PlayerStatus `json:"players"`
}

func update(w http.ResponseWriter, r *http.Request) {
	log.Println(r.URL)
	if SetCorsHeaders(w.Header(), r) {
		return
	}

	req := UpdateReq{}
	if !ReadJson(w, r, &req) {
		return
	}

	if len(req.Id) == 0 {
		http.Error(w, "missing id", http.StatusBadRequest)
		return
	}

	mu.Lock()
	defer mu.Unlock()

	id := pid(req.Id)
	player, ok := players[id]
	if !ok {
		http.Error(w, "invalid id", http.StatusBadRequest)
		return
	}

	server, ok := servers[player.sid]
	if !ok {
		http.Error(w, "no server for that player", http.StatusInternalServerError)
		return
	}

	now := time.Now()
	player.lastScore = req.Score
	player.lastX = req.X
	player.lastY = req.Y
	player.lastUpdate = now

	results := []PlayerStatus{}
	playersToGc := []pid{}
	for _, player := range server.players {
		// GC players who haven't posted in 30 seconds.
		if player.lastUpdate.Before(now.Add(-30 * time.Second)) {
			playersToGc = append(playersToGc, player.pid)
			continue
		}
		// Omit players who haven't posted in 5 seconds or are dead.
		if player.lastScore == 0 || player.lastUpdate.Before(now.Add(-5*time.Second)) {
			continue
		}

		results = append(results, PlayerStatus{
			Name:  player.name,
			Color: player.color,
			X:     player.lastX,
			Y:     player.lastY,
			Score: player.lastScore,
			IsMe:  (player.pid == id),
		})
	}

	for _, gc := range playersToGc {
		delete(server.players, gc)
		delete(players, gc)
	}

	sort.Sort(sort.Reverse(byScore(results))) // by score descending
	JsonRespond(w, &UpdateRsp{
		Players: results,
	})
}

type byScore []PlayerStatus

func (p byScore) Len() int {
	return len(p)
}
func (p byScore) Less(i, j int) bool {
	pi := p[i]
	pj := p[j]
	if pi.Score < pj.Score {
		return true
	} else if pi.Score > pj.Score {
		return false
	}

	return pi.Name < pj.Name
}
func (p byScore) Swap(i, j int) {
	p[i], p[j] = p[j], p[i]
}

func main() {
	addr := flag.String("addr", ":8080", "http service address")
	flag.Parse()
	http.HandleFunc("/getId", getId)
	http.HandleFunc("/update", update)
	err := http.ListenAndServe(*addr, nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}
<commit_msg>don't require a name<commit_after>package main

import (
	"flag"
	"log"
	"math/rand"
	"net/http"
	"sort"
	"strconv"
	"sync"
	"time"
)

type sid string
type pid string

type Server struct {
	sid     sid
	players map[pid]*Player
}

type Player struct {
	pid          pid
	sid          sid
	name         string
	color        uint32
	lastUpdate   time.Time
	lastX, lastY float64
	lastScore    int32
}

var mu sync.Mutex
var rnd = rand.New(rand.NewSource(time.Now().Unix()))
var players = map[pid]*Player{}
var servers = map[sid]*Server{}

func newId() string {
	return strconv.FormatInt(rnd.Int63(), 16)
}

type GetIdReq struct {
	Sid   string  `json:"sid"`
	Name  string  `json:"name"`
	Color uint32  `json:"color"`
	X     float64 `json:"x"`
	Y     float64 `json:"y"`
	Score int32   `json:"score"`
}

type GetIdRsp struct {
	Id string `json:"id"`
}

func getId(w http.ResponseWriter, r *http.Request) {
	log.Println(r.URL)
	if SetCorsHeaders(w.Header(), r) {
		return
	}

	req := GetIdReq{}
	if !ReadJson(w, r, &req) {
		return
	}

	if len(req.Sid) == 0 {
		http.Error(w, "missing sId", http.StatusBadRequest)
		return
	}

	mu.Lock()
	defer mu.Unlock()

	sid := sid(req.Sid)
	id := pid(newId())
	server, ok := servers[sid]
	if !ok {
		server = &Server{
			sid:     sid,
			players: map[pid]*Player{},
		}
		servers[sid] = server
	}

	player := &Player{
		pid:        id,
		sid:        sid,
		name:       req.Name,
		color:      req.Color,
		lastX:      req.X,
		lastY:      req.Y,
		lastScore:  req.Score,
		lastUpdate: time.Now(),
	}
	players[id] = player
	server.players[id] = player

	JsonRespond(w, &GetIdRsp{
		Id: string(player.pid),
	})
}

type UpdateReq struct {
	Id    string  `json:"id"`
	X     float64 `json:"x"`
	Y     float64 `json:"y"`
	Score int32   `json:"score"`
}

type PlayerStatus struct {
	Name  string  `json:"name"`
	Color uint32  `json:"color"`
	X     float64 `json:"x"`
	Y     float64 `json:"y"`
	Score int32   `json:"score"`
	IsMe  bool    `json:"is_me"`
}

type UpdateRsp struct {
	Players []PlayerStatus `json:"players"`
}

func update(w http.ResponseWriter, r *http.Request) {
	log.Println(r.URL)
	if SetCorsHeaders(w.Header(), r) {
		return
	}

	req := UpdateReq{}
	if !ReadJson(w, r, &req) {
		return
	}

	if len(req.Id) == 0 {
		http.Error(w, "missing id", http.StatusBadRequest)
		return
	}

	mu.Lock()
	defer mu.Unlock()

	id := pid(req.Id)
	player, ok := players[id]
	if !ok {
		http.Error(w, "invalid id", http.StatusBadRequest)
		return
	}

	server, ok := servers[player.sid]
	if !ok {
		http.Error(w, "no server for that player", http.StatusInternalServerError)
		return
	}

	now := time.Now()
	player.lastScore = req.Score
	player.lastX = req.X
	player.lastY = req.Y
	player.lastUpdate = now

	results := []PlayerStatus{}
	playersToGc := []pid{}
	for _, player := range server.players {
		// GC players who haven't posted in 30 seconds.
		if player.lastUpdate.Before(now.Add(-30 * time.Second)) {
			playersToGc = append(playersToGc, player.pid)
			continue
		}
		// Omit players who haven't posted in 5 seconds or are dead.
		if player.lastScore == 0 || player.lastUpdate.Before(now.Add(-5*time.Second)) {
			continue
		}

		results = append(results, PlayerStatus{
			Name:  player.name,
			Color: player.color,
			X:     player.lastX,
			Y:     player.lastY,
			Score: player.lastScore,
			IsMe:  (player.pid == id),
		})
	}

	for _, gc := range playersToGc {
		delete(server.players, gc)
		delete(players, gc)
	}

	sort.Sort(sort.Reverse(byScore(results))) // by score descending
	JsonRespond(w, &UpdateRsp{
		Players: results,
	})
}

type byScore []PlayerStatus

func (p byScore) Len() int {
	return len(p)
}
func (p byScore) Less(i, j int) bool {
	pi := p[i]
	pj := p[j]
	if pi.Score < pj.Score {
		return true
	} else if pi.Score > pj.Score {
		return false
	}

	return pi.Name < pj.Name
}
func (p byScore) Swap(i, j int) {
	p[i], p[j] = p[j], p[i]
}

func main() {
	addr := flag.String("addr", ":8080", "http service address")
	flag.Parse()
	http.HandleFunc("/getId", getId)
	http.HandleFunc("/update", update)
	err := http.ListenAndServe(*addr, nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}
<|endoftext|>


<commit_before>package profile

import (
	"fmt"
	"regexp"
	"strconv"
	"strings"

	"github.com/tormoder/fit/internal/types"
)

var camelRegex = regexp.MustCompile("[0-9A-Za-z]+")

func toCamelCase(s string) string {
	chunks := camelRegex.FindAllString(s, -1)
	for i, val := range chunks {
		chunks[i] = strings.Title(val)
	}
	return strings.Join(chunks, "")
}

var typeQuirks = map[string]string{
	"activity": "activity_mode",
}

func isTimestamp(name string) (types.Kind, bool) {
	if name == "date_time" {
		return types.TimeUTC, true
	}
	if name == "local_date_time" {
		return types.TimeLocal, true
	}
	return 0, false
}

func isCoordinate(name string) (types.Kind, bool) {
	if strings.HasSuffix(name, "_lat") {
		return types.Lat, true
	}
	if strings.HasSuffix(name, "_long") {
		return types.Lng, true
	}
	return 0, false
}

func TransformTypes(ptypes []*PType) (map[string]*Type, error) {
	types := make(map[string]*Type)
	for _, pt := range ptypes {
		t := Type{data: pt}
		skip, err := t.transform()
		if err != nil {
			return nil, err
		}
		if skip {
			continue
		}
		types[t.Name] = &t
	}

	return types, nil
}

func (t *Type) transform() (skip bool, err error) {
	_, isTS := isTimestamp(t.data.Header[tNAME])
	if isTS {
		return true, nil
	}

	name := t.data.Header[tNAME]
	if name == "" {
		return false, fmt.Errorf(
			"found empty type name in header %q",
			t.data.Header)
	}
	t.OrigName = name
	t.Name = toCamelCase(name)

	t.BaseType, err = types.BaseFromString(t.data.Header[tBTYPE])
	if err != nil {
		return false, err
	}

	for _, f := range t.data.Fields {
		vt := ValueTriple{
			Name:    toCamelCase(f[tVALNAME]),
			Value:   f[tVAL],
			Comment: f[tCOMMENT],
		}
		t.Values = append(t.Values, vt)
	}

	if renamed, found := typeQuirks[name]; found {
		t.Name = toCamelCase(renamed)
	}

	return false, nil
}

func TransformMsgs(pmsgs []*PMsg, ftypes map[string]*Type) ([]*Msg, error) {
	var msgs []*Msg
	for _, pmsg := range pmsgs {
		msg := Msg{
			Name:        pmsg.Header[mMSGNAME],
			FieldByName: make(map[string]*Field),
		}

		if len(msg.Name) == 0 {
			return nil, fmt.Errorf(
				"found empty message name in header %q",
				pmsg.Header)
		}
		msg.CCName = toCamelCase(msg.Name)
		debugln("transforming message", msg.CCName)

		for _, pfield := range pmsg.Fields {
			f := &Field{data: pfield.Field}
			skip, err := f.transform(false, ftypes)
			if err != nil {
				return nil, err
			}
			if skip {
				continue
			}
			msg.Fields = append(msg.Fields, f)
			msg.FieldByName[f.CCName] = f
			if len(pfield.Subfields) == 0 {
				continue
			}

			for _, sfield := range pfield.Subfields {
				sf := &Field{data: sfield}
				skip, err := sf.transform(true, ftypes)
				if err != nil {
					return nil, fmt.Errorf("error parsing subfield: %v", err)
				}
				if skip {
					continue
				}
				f.Subfields = append(f.Subfields, sf)
			}
		}
		msgs = append(msgs, &msg)
	}

	return msgs, nil
}

func (f *Field) transform(subfield bool, ftypes map[string]*Type) (skip bool, err error) {
	if f.data[mEXAMPLE] == "" {
		return true, nil
	}

	f.DefNum = f.data[mFDEFN]
	f.Name = f.data[mFNAME]
	f.CCName = toCamelCase(f.Name)

	f.parseArray()

	err = f.parseType(ftypes)
	if err != nil {
		return false, err
	}

	f.Units = f.data[mUNITS]
	f.Comment = f.data[mCOMMENT]
	f.Example = f.data[mEXAMPLE]

	if subfield {
		f.parseRefFields()
	}

	if f.data[mCOMPS] == "" {
		f.parseScaleOffset()
		return false, nil
	}

	return false, f.parseComponents(ftypes)
}

func (f *Field) parseArray() {
	arrayStr := strings.TrimFunc(
		f.data[mARRAY], func(r rune) bool {
			if r == '[' || r == ']' {
				return true
			}
			return false
		})
	switch arrayStr {
	case "":
		f.Array = "0"
	case "N":
		f.Array = "255"
	default:
		f.Array = arrayStr
	}
}

func (f *Field) parseType(ftypes map[string]*Type) error {
	array := f.Array != "0"

	coordKind, isCoord := isCoordinate(f.Name)
	if isCoord {
		f.FType = types.Make(coordKind, array)
		f.TypeName = f.FType.GoType()
		return nil
	}

	originalTypeName := f.data[mFTYPE]
	if rewritten, tfound := typeQuirks[originalTypeName]; tfound {
		f.TypeName = toCamelCase(rewritten)
	} else {
		f.TypeName = toCamelCase(originalTypeName)
	}

	tsKind, isTimestamp := isTimestamp(originalTypeName)
	if isTimestamp {
		f.FType = types.Make(tsKind, array)
		f.TypeName = f.FType.GoType()
		return nil
	}

	if f.TypeName == "Bool" {
		f.FType = types.MakeNative(types.BaseEnum, array)
		return nil
	}

	typeDef, found := ftypes[f.TypeName]
	if found {
		f.FType = types.MakeNative(typeDef.BaseType, array)
		if array {
			f.TypeName = "[]" + f.TypeName
		}
		return nil
	}

	// Assume base type.
	baseType, err := types.BaseFromString(originalTypeName)
	if err != nil {
		return err
	}
	f.FType = types.MakeNative(baseType, array)
	f.TypeName = f.FType.GoType()

	return nil
}

func (f *Field) parseRefFields() {
	f.RefFieldName = strings.Split(f.data[mRFNAME], ",")
	if len(f.RefFieldName) > 0 {
		for i, rfn := range f.RefFieldName {
			tmp := strings.TrimSpace(rfn)
			f.RefFieldName[i] = toCamelCase(tmp)
		}
	}

	f.RefFieldValue = strings.Split(f.data[mRFVAL], ",")
	if len(f.RefFieldValue) > 0 {
		for i, rfv := range f.RefFieldValue {
			tmp := strings.TrimSpace(rfv)
			f.RefFieldValue[i] = toCamelCase(tmp)
		}
	}
}

func (f *Field) parseScaleOffset() {
	if f.data[mSCALE] == "" {
		return
	}
	f.Scale = f.data[mSCALE]
	if f.data[mOFFSET] != "" {
		f.Offset = f.data[mOFFSET]
	}
}

func (f *Field) parseComponents(ftypes map[string]*Type) error {
	if f.data[mCOMPS] == "" {
		return nil
	}

	debugln("parsing components for field", f.CCName)

	switch f.FType.BaseType() {
	case types.BaseUint8, types.BaseUint16, types.BaseUint32:
	case types.BaseByte:
		if !f.FType.Array() {
			return fmt.Errorf("parseComponents: base type was byte but not an array")
		}
	default:
		return fmt.Errorf(
			"parseComponents: unhandled base type (%s) for field %s",
			f.FType.BaseType(), f.CCName)
	}

	components := strings.Split(f.data[mCOMPS], ",")
	if len(components) == 0 {
		return fmt.Errorf("parseComponents: zero components after string split")
	}

	bits := strings.Split(f.data[mBITS], ",")
	if len(components) != len(bits) {
		return fmt.Errorf(
			"parseComponents: number of components (%d) and bits (%d) differ",
			len(components), len(bits))
	}

	accumulate := strings.Split(f.data[mACCUMU], ",")
	if len(accumulate) == 1 && accumulate[0] == "" {
		accumulate = nil
	}

	if len(accumulate) > 0 && (len(accumulate) != len(components)) {
		return fmt.Errorf(
			"parseComponents: number of components (%d) and accumulate flags (%d) differ",
			len(components), len(accumulate))
	}

	f.Components = make([]Component, len(components))

	var (
		err       error
		bitsTotal int
	)

	for i, comp := range components {
		f.Components[i].Name = strings.TrimSpace(comp)
		f.Components[i].Name = toCamelCase(f.Components[i].Name)
		f.Components[i].Bits = strings.TrimSpace(bits[i])
		f.Components[i].BitsInt, err = strconv.Atoi(f.Components[i].Bits)
		if err != nil {
			return fmt.Errorf("parseComponents: error converting bit to integer: %v", err)
		}
		bitsTotal += f.Components[i].BitsInt
		if len(accumulate) == len(components) {
			tmp := strings.TrimSpace(accumulate[i])
			f.Components[i].Accumulate, err = strconv.ParseBool(tmp)
			if err != nil {
				return fmt.Errorf("parseComponents: %v", err)
			}
		}
	}

	if bitsTotal > 32 || bitsTotal < 0 {
		return fmt.Errorf("parseComponents: illegal size for total number of bits: %d", bitsTotal)
	}

	if len(components) == 1 {
		// Set any scale on the "main" field.
		// TODO(tormoder): Verify that this is correct.
		f.parseScaleOffset()
		return nil
	}

	cscale := strings.Split(f.data[mSCALE], ",")
	coffset := strings.Split(f.data[mOFFSET], ",")

	if len(coffset) == 1 && coffset[0] == "" {
		coffset = nil
	}
	if len(cscale) != len(components) {
		return fmt.Errorf(
			"parseComponents: number of components (%d) and scales (%d) differ",
			len(components), len(cscale))
	}
	if len(coffset) != 0 && len(coffset) != len(components) {
		return fmt.Errorf(
			"parseComponents: #offset != 0 and number of components (%d) and offsets (%d) differ",
			len(components), len(coffset))
	}

	for i := range f.Components {
		f.Components[i].Scale = strings.TrimSpace(cscale[i])
		if len(coffset) == 0 {
			continue
		}
		f.Components[i].Offset = strings.TrimSpace(coffset[i])
	}

	return nil
}
<commit_msg>internal/profile: handle bits (new sdk)<commit_after>package profile

import (
	"fmt"
	"regexp"
	"strconv"
	"strings"

	"github.com/tormoder/fit/internal/types"
)

var camelRegex = regexp.MustCompile("[0-9A-Za-z]+")

func toCamelCase(s string) string {
	chunks := camelRegex.FindAllString(s, -1)
	for i, val := range chunks {
		chunks[i] = strings.Title(val)
	}
	return strings.Join(chunks, "")
}

var typeQuirks = map[string]string{
	"activity": "activity_mode",
}

func isTimestamp(name string) (types.Kind, bool) {
	if name == "date_time" {
		return types.TimeUTC, true
	}
	if name == "local_date_time" {
		return types.TimeLocal, true
	}
	return 0, false
}

func isCoordinate(name string) (types.Kind, bool) {
	if strings.HasSuffix(name, "_lat") {
		return types.Lat, true
	}
	if strings.HasSuffix(name, "_long") {
		return types.Lng, true
	}
	return 0, false
}

func TransformTypes(ptypes []*PType) (map[string]*Type, error) {
	types := make(map[string]*Type)
	for _, pt := range ptypes {
		t := Type{data: pt}
		skip, err := t.transform()
		if err != nil {
			return nil, err
		}
		if skip {
			continue
		}
		types[t.Name] = &t
	}

	return types, nil
}

func (t *Type) transform() (skip bool, err error) {
	_, isTS := isTimestamp(t.data.Header[tNAME])
	if isTS {
		return true, nil
	}

	name := t.data.Header[tNAME]
	if name == "" {
		return false, fmt.Errorf(
			"found empty type name in header %q",
			t.data.Header)
	}
	t.OrigName = name
	t.Name = toCamelCase(name)

	t.BaseType, err = types.BaseFromString(t.data.Header[tBTYPE])
	if err != nil {
		return false, err
	}

	for _, f := range t.data.Fields {
		vt := ValueTriple{
			Name:    toCamelCase(f[tVALNAME]),
			Value:   f[tVAL],
			Comment: f[tCOMMENT],
		}
		t.Values = append(t.Values, vt)
	}

	if renamed, found := typeQuirks[name]; found {
		t.Name = toCamelCase(renamed)
	}

	return false, nil
}

func TransformMsgs(pmsgs []*PMsg, ftypes map[string]*Type) ([]*Msg, error) {
	var msgs []*Msg
	for _, pmsg := range pmsgs {
		msg := Msg{
			Name:        pmsg.Header[mMSGNAME],
			FieldByName: make(map[string]*Field),
		}

		if len(msg.Name) == 0 {
			return nil, fmt.Errorf(
				"found empty message name in header %q",
				pmsg.Header)
		}
		msg.CCName = toCamelCase(msg.Name)
		debugln("transforming message", msg.CCName)

		for _, pfield := range pmsg.Fields {
			f := &Field{data: pfield.Field}
			skip, err := f.transform(false, ftypes)
			if err != nil {
				return nil, err
			}
			if skip {
				continue
			}
			msg.Fields = append(msg.Fields, f)
			msg.FieldByName[f.CCName] = f
			if len(pfield.Subfields) == 0 {
				continue
			}

			for _, sfield := range pfield.Subfields {
				sf := &Field{data: sfield}
				skip, err := sf.transform(true, ftypes)
				if err != nil {
					return nil, fmt.Errorf("error parsing subfield: %v", err)
				}
				if skip {
					continue
				}
				f.Subfields = append(f.Subfields, sf)
			}
		}
		msgs = append(msgs, &msg)
	}

	return msgs, nil
}

func (f *Field) transform(subfield bool, ftypes map[string]*Type) (skip bool, err error) {
	if f.data[mEXAMPLE] == "" {
		return true, nil
	}

	f.DefNum = f.data[mFDEFN]
	f.Name = f.data[mFNAME]
	f.CCName = toCamelCase(f.Name)

	f.parseArray()

	err = f.parseType(ftypes)
	if err != nil {
		return false, err
	}

	f.Units = f.data[mUNITS]
	f.Comment = f.data[mCOMMENT]
	f.Example = f.data[mEXAMPLE]

	if subfield {
		f.parseRefFields()
	}

	if f.data[mCOMPS] == "" {
		f.parseScaleOffset()
		return false, nil
	}

	return false, f.parseComponents(ftypes)
}

func (f *Field) parseArray() {
	arrayStr := strings.TrimFunc(
		f.data[mARRAY], func(r rune) bool {
			if r == '[' || r == ']' {
				return true
			}
			return false
		})
	switch arrayStr {
	case "":
		f.Array = "0"
	case "N":
		f.Array = "255"
	default:
		f.Array = arrayStr
	}
}

func (f *Field) parseType(ftypes map[string]*Type) error {
	array := f.Array != "0"

	coordKind, isCoord := isCoordinate(f.Name)
	if isCoord {
		f.FType = types.Make(coordKind, array)
		f.TypeName = f.FType.GoType()
		return nil
	}

	originalTypeName := f.data[mFTYPE]
	if rewritten, tfound := typeQuirks[originalTypeName]; tfound {
		f.TypeName = toCamelCase(rewritten)
	} else {
		f.TypeName = toCamelCase(originalTypeName)
	}

	tsKind, isTimestamp := isTimestamp(originalTypeName)
	if isTimestamp {
		f.FType = types.Make(tsKind, array)
		f.TypeName = f.FType.GoType()
		return nil
	}

	if f.TypeName == "Bool" {
		f.FType = types.MakeNative(types.BaseEnum, array)
		return nil
	}

	typeDef, found := ftypes[f.TypeName]
	if found {
		f.FType = types.MakeNative(typeDef.BaseType, array)
		if array {
			f.TypeName = "[]" + f.TypeName
		}
		return nil
	}

	// Assume base type.
	baseType, err := types.BaseFromString(originalTypeName)
	if err != nil {
		return err
	}
	f.FType = types.MakeNative(baseType, array)
	f.TypeName = f.FType.GoType()

	return nil
}

func (f *Field) parseRefFields() {
	f.RefFieldName = strings.Split(f.data[mRFNAME], ",")
	if len(f.RefFieldName) > 0 {
		for i, rfn := range f.RefFieldName {
			tmp := strings.TrimSpace(rfn)
			f.RefFieldName[i] = toCamelCase(tmp)
		}
	}

	f.RefFieldValue = strings.Split(f.data[mRFVAL], ",")
	if len(f.RefFieldValue) > 0 {
		for i, rfv := range f.RefFieldValue {
			tmp := strings.TrimSpace(rfv)
			f.RefFieldValue[i] = toCamelCase(tmp)
		}
	}
}

func (f *Field) parseScaleOffset() {
	if f.data[mSCALE] == "" {
		return
	}
	f.Scale = f.data[mSCALE]
	if f.data[mOFFSET] != "" {
		f.Offset = f.data[mOFFSET]
	}
}

func (f *Field) parseComponents(ftypes map[string]*Type) error {
	if f.data[mCOMPS] == "" {
		return nil
	}

	debugln("parsing components for field", f.CCName)

	switch f.FType.BaseType() {
	case types.BaseUint8, types.BaseUint16, types.BaseUint32:
	case types.BaseByte:
		if !f.FType.Array() {
			return fmt.Errorf("parseComponents: base type was byte but not an array")
		}
	default:
		return fmt.Errorf(
			"parseComponents: unhandled base type (%s) for field %s",
			f.FType.BaseType(), f.CCName)
	}

	components := strings.Split(f.data[mCOMPS], ",")
	if len(components) == 0 {
		return fmt.Errorf("parseComponents: zero components after string split")
	}

	bitsFull := f.data[mBITS]
	if new, rewrite := bitsRewrite[bitsFull]; rewrite {
		bitsFull = new
	}
	bits := strings.Split(bitsFull, ",")

	if len(components) != len(bits) {
		return fmt.Errorf("parseComponents: number of components (%d) and bits (%d) differ", len(components), len(bits))
	}

	accumulate := strings.Split(f.data[mACCUMU], ",")
	if len(accumulate) == 1 && accumulate[0] == "" {
		accumulate = nil
	}

	if len(accumulate) > 0 && (len(accumulate) != len(components)) {
		return fmt.Errorf(
			"parseComponents: number of components (%d) and accumulate flags (%d) differ",
			len(components), len(accumulate))
	}

	f.Components = make([]Component, len(components))

	var (
		err       error
		bitsTotal int
	)

	for i, comp := range components {
		f.Components[i].Name = strings.TrimSpace(comp)
		f.Components[i].Name = toCamelCase(f.Components[i].Name)
		f.Components[i].Bits = strings.TrimSpace(bits[i])
		f.Components[i].BitsInt, err = strconv.Atoi(f.Components[i].Bits)
		if err != nil {
			return fmt.Errorf("parseComponents: error converting bit to integer: %v", err)
		}
		bitsTotal += f.Components[i].BitsInt
		if len(accumulate) == len(components) {
			tmp := strings.TrimSpace(accumulate[i])
			f.Components[i].Accumulate, err = strconv.ParseBool(tmp)
			if err != nil {
				return fmt.Errorf("parseComponents: %v", err)
			}
		}
	}

	if bitsTotal > 32 || bitsTotal < 0 {
		return fmt.Errorf("parseComponents: illegal size for total number of bits: %d", bitsTotal)
	}

	if len(components) == 1 {
		// Set any scale on the "main" field.
		// TODO(tormoder): Verify that this is correct.
		f.parseScaleOffset()
		return nil
	}

	cscale := strings.Split(f.data[mSCALE], ",")
	coffset := strings.Split(f.data[mOFFSET], ",")

	if len(coffset) == 1 && coffset[0] == "" {
		coffset = nil
	}
	if len(cscale) != len(components) {
		return fmt.Errorf(
			"parseComponents: number of components (%d) and scales (%d) differ",
			len(components), len(cscale))
	}
	if len(coffset) != 0 && len(coffset) != len(components) {
		return fmt.Errorf(
			"parseComponents: #offset != 0 and number of components (%d) and offsets (%d) differ",
			len(components), len(coffset))
	}

	for i := range f.Components {
		f.Components[i].Scale = strings.TrimSpace(cscale[i])
		if len(coffset) == 0 {
			continue
		}
		f.Components[i].Offset = strings.TrimSpace(coffset[i])
	}

	return nil
}

var bitsRewrite = map[string]string{
	"1616":     "16,16",
	"88888888": "8,8,8,8,8,8,8,8",
	"53":       "5,3",
	"44":       "4,4",
}
<|endoftext|>


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

// The exec package runs external commands.
package exec

import (
	"os"
	"strconv"
)

// Arguments to Run.
const (
	DevNull = iota
	PassThrough
	Pipe
	MergeWithStdout
)

// A Cmd represents a running command.
// Stdin, Stdout, and Stderr are Files representing pipes
// connected to the running command's standard input, output, and error,
// or else nil, depending on the arguments to Run.
// Process represents the underlying operating system process.
type Cmd struct {
	Stdin   *os.File
	Stdout  *os.File
	Stderr  *os.File
	Process *os.Process
}

// PathError records the name of a binary that was not
// found on the current $PATH.
type PathError struct {
	Name string
}

func (e *PathError) String() string {
	return "command " + strconv.Quote(e.Name) + " not found in $PATH"
}

// Given mode (DevNull, etc), return file for child
// and file to record in Cmd structure.
func modeToFiles(mode, fd int) (*os.File, *os.File, os.Error) {
	switch mode {
	case DevNull:
		rw := os.O_WRONLY
		if fd == 0 {
			rw = os.O_RDONLY
		}
		f, err := os.Open(os.DevNull, rw, 0)
		return f, nil, err
	case PassThrough:
		switch fd {
		case 0:
			return os.Stdin, nil, nil
		case 1:
			return os.Stdout, nil, nil
		case 2:
			return os.Stderr, nil, nil
		}
	case Pipe:
		r, w, err := os.Pipe()
		if err != nil {
			return nil, nil, err
		}
		if fd == 0 {
			return r, w, nil
		}
		return w, r, nil
	}
	return nil, nil, os.EINVAL
}

// Run starts the named binary running with
// arguments argv and environment envv.
// If dir is not empty, the child chdirs into the
// directory before executing the binary.
// It returns a pointer to a new Cmd representing
// the command or an error.
//
// The parameters stdin, stdout, and stderr
// specify how to handle standard input, output, and error.
// The choices are DevNull (connect to /dev/null),
// PassThrough (connect to the current process's standard stream),
// Pipe (connect to an operating system pipe), and
// MergeWithStdout (only for standard error; use the same
// file descriptor as was used for standard output).
// If a parameter is Pipe, then the corresponding field (Stdin, Stdout, Stderr)
// of the returned Cmd is the other end of the pipe.
// Otherwise the field in Cmd is nil.
func Run(name string, argv, envv []string, dir string, stdin, stdout, stderr int) (c *Cmd, err os.Error) {
	c = new(Cmd)
	var fd [3]*os.File

	if fd[0], c.Stdin, err = modeToFiles(stdin, 0); err != nil {
		goto Error
	}
	if fd[1], c.Stdout, err = modeToFiles(stdout, 1); err != nil {
		goto Error
	}
	if stderr == MergeWithStdout {
		fd[2] = fd[1]
	} else if fd[2], c.Stderr, err = modeToFiles(stderr, 2); err != nil {
		goto Error
	}

	// Run command.
	c.Process, err = os.StartProcess(name, argv, &os.ProcAttr{Dir: dir, Files: fd[:], Env: envv})
	if err != nil {
		goto Error
	}
	if fd[0] != os.Stdin {
		fd[0].Close()
	}
	if fd[1] != os.Stdout {
		fd[1].Close()
	}
	if fd[2] != os.Stderr && fd[2] != fd[1] {
		fd[2].Close()
	}
	return c, nil

Error:
	if fd[0] != os.Stdin && fd[0] != nil {
		fd[0].Close()
	}
	if fd[1] != os.Stdout && fd[1] != nil {
		fd[1].Close()
	}
	if fd[2] != os.Stderr && fd[2] != nil && fd[2] != fd[1] {
		fd[2].Close()
	}
	if c.Stdin != nil {
		c.Stdin.Close()
	}
	if c.Stdout != nil {
		c.Stdout.Close()
	}
	if c.Stderr != nil {
		c.Stderr.Close()
	}
	if c.Process != nil {
		c.Process.Release()
	}
	return nil, err
}

// Wait waits for the running command c,
// returning the Waitmsg returned when the process exits.
// The options are passed to the process's Wait method.
// Setting options to 0 waits for c to exit;
// other options cause Wait to return for other
// process events; see package os for details.
func (c *Cmd) Wait(options int) (*os.Waitmsg, os.Error) {
	if c.Process == nil {
		return nil, os.ErrorString("exec: invalid use of Cmd.Wait")
	}
	w, err := c.Process.Wait(options)
	if w != nil && (w.Exited() || w.Signaled()) {
		c.Process.Release()
		c.Process = nil
	}
	return w, err
}

// Close waits for the running command c to exit,
// if it hasn't already, and then closes the non-nil file descriptors
// c.Stdin, c.Stdout, and c.Stderr.
func (c *Cmd) Close() os.Error {
	if c.Process != nil {
		// Loop on interrupt, but
		// ignore other errors -- maybe
		// caller has already waited for pid.
		_, err := c.Wait(0)
		for err == os.EINTR {
			_, err = c.Wait(0)
		}
	}

	// Close the FDs that are still open.
	var err os.Error
	if c.Stdin != nil && c.Stdin.Fd() >= 0 {
		if err1 := c.Stdin.Close(); err1 != nil {
			err = err1
		}
	}
	if c.Stdout != nil && c.Stdout.Fd() >= 0 {
		if err1 := c.Stdout.Close(); err1 != nil && err != nil {
			err = err1
		}
	}
	if c.Stderr != nil && c.Stderr != c.Stdout && c.Stderr.Fd() >= 0 {
		if err1 := c.Stderr.Close(); err1 != nil && err != nil {
			err = err1
		}
	}
	return err
}
<commit_msg>Fixed doc line after Brad's suggestion.<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// The exec package runs external commands.
package exec

import (
	"os"
	"strconv"
)

// Arguments to Run.
const (
	DevNull = iota
	PassThrough
	Pipe
	MergeWithStdout
)

// A Cmd represents a running command.
// Stdin, Stdout, and Stderr are Files representing pipes
// connected to the running command's standard input, output, and error,
// or else nil, depending on the arguments to Run.
// Process represents the underlying operating system process.
type Cmd struct {
	Stdin   *os.File
	Stdout  *os.File
	Stderr  *os.File
	Process *os.Process
}

// PathError records the name of a binary that was not
// found on the current $PATH.
type PathError struct {
	Name string
}

func (e *PathError) String() string {
	return "command " + strconv.Quote(e.Name) + " not found in $PATH"
}

// Given mode (DevNull, etc), return file for child
// and file to record in Cmd structure.
func modeToFiles(mode, fd int) (*os.File, *os.File, os.Error) {
	switch mode {
	case DevNull:
		rw := os.O_WRONLY
		if fd == 0 {
			rw = os.O_RDONLY
		}
		f, err := os.Open(os.DevNull, rw, 0)
		return f, nil, err
	case PassThrough:
		switch fd {
		case 0:
			return os.Stdin, nil, nil
		case 1:
			return os.Stdout, nil, nil
		case 2:
			return os.Stderr, nil, nil
		}
	case Pipe:
		r, w, err := os.Pipe()
		if err != nil {
			return nil, nil, err
		}
		if fd == 0 {
			return r, w, nil
		}
		return w, r, nil
	}
	return nil, nil, os.EINVAL
}

// Run starts the named binary running with
// arguments argv and environment envv.
// If the dir parameter is not empty, the child chdirs
// into the directory before executing the binary.
// It returns a pointer to a new Cmd representing
// the command or an error.
//
// The parameters stdin, stdout, and stderr
// specify how to handle standard input, output, and error.
// The choices are DevNull (connect to /dev/null),
// PassThrough (connect to the current process's standard stream),
// Pipe (connect to an operating system pipe), and
// MergeWithStdout (only for standard error; use the same
// file descriptor as was used for standard output).
// If a parameter is Pipe, then the corresponding field (Stdin, Stdout, Stderr)
// of the returned Cmd is the other end of the pipe.
// Otherwise the field in Cmd is nil.
func Run(name string, argv, envv []string, dir string, stdin, stdout, stderr int) (c *Cmd, err os.Error) {
	c = new(Cmd)
	var fd [3]*os.File

	if fd[0], c.Stdin, err = modeToFiles(stdin, 0); err != nil {
		goto Error
	}
	if fd[1], c.Stdout, err = modeToFiles(stdout, 1); err != nil {
		goto Error
	}
	if stderr == MergeWithStdout {
		fd[2] = fd[1]
	} else if fd[2], c.Stderr, err = modeToFiles(stderr, 2); err != nil {
		goto Error
	}

	// Run command.
	c.Process, err = os.StartProcess(name, argv, &os.ProcAttr{Dir: dir, Files: fd[:], Env: envv})
	if err != nil {
		goto Error
	}
	if fd[0] != os.Stdin {
		fd[0].Close()
	}
	if fd[1] != os.Stdout {
		fd[1].Close()
	}
	if fd[2] != os.Stderr && fd[2] != fd[1] {
		fd[2].Close()
	}
	return c, nil

Error:
	if fd[0] != os.Stdin && fd[0] != nil {
		fd[0].Close()
	}
	if fd[1] != os.Stdout && fd[1] != nil {
		fd[1].Close()
	}
	if fd[2] != os.Stderr && fd[2] != nil && fd[2] != fd[1] {
		fd[2].Close()
	}
	if c.Stdin != nil {
		c.Stdin.Close()
	}
	if c.Stdout != nil {
		c.Stdout.Close()
	}
	if c.Stderr != nil {
		c.Stderr.Close()
	}
	if c.Process != nil {
		c.Process.Release()
	}
	return nil, err
}

// Wait waits for the running command c,
// returning the Waitmsg returned when the process exits.
// The options are passed to the process's Wait method.
// Setting options to 0 waits for c to exit;
// other options cause Wait to return for other
// process events; see package os for details.
func (c *Cmd) Wait(options int) (*os.Waitmsg, os.Error) {
	if c.Process == nil {
		return nil, os.ErrorString("exec: invalid use of Cmd.Wait")
	}
	w, err := c.Process.Wait(options)
	if w != nil && (w.Exited() || w.Signaled()) {
		c.Process.Release()
		c.Process = nil
	}
	return w, err
}

// Close waits for the running command c to exit,
// if it hasn't already, and then closes the non-nil file descriptors
// c.Stdin, c.Stdout, and c.Stderr.
func (c *Cmd) Close() os.Error {
	if c.Process != nil {
		// Loop on interrupt, but
		// ignore other errors -- maybe
		// caller has already waited for pid.
		_, err := c.Wait(0)
		for err == os.EINTR {
			_, err = c.Wait(0)
		}
	}

	// Close the FDs that are still open.
	var err os.Error
	if c.Stdin != nil && c.Stdin.Fd() >= 0 {
		if err1 := c.Stdin.Close(); err1 != nil {
			err = err1
		}
	}
	if c.Stdout != nil && c.Stdout.Fd() >= 0 {
		if err1 := c.Stdout.Close(); err1 != nil && err != nil {
			err = err1
		}
	}
	if c.Stderr != nil && c.Stderr != c.Stdout && c.Stderr.Fd() >= 0 {
		if err1 := c.Stderr.Close(); err1 != nil && err != nil {
			err = err1
		}
	}
	return err
}
<|endoftext|>


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

package cloud_test

import (
	"strings"

	jc "github.com/juju/testing/checkers"
	gc "gopkg.in/check.v1"

	jujucloud "github.com/juju/juju/cloud"
	"github.com/juju/juju/cmd/juju/cloud"
	"github.com/juju/juju/juju/osenv"
	"github.com/juju/juju/jujuclient/jujuclienttesting"
	"github.com/juju/juju/testing"
)

type defaultCredentialSuite struct {
	testing.BaseSuite
}

var _ = gc.Suite(&defaultCredentialSuite{})

func (s *defaultCredentialSuite) SetUpTest(c *gc.C) {
	origHome := osenv.SetJujuXDGDataHome(c.MkDir())
	s.AddCleanup(func(*gc.C) { osenv.SetJujuXDGDataHome(origHome) })
}

func (s *defaultCredentialSuite) TestBadArgs(c *gc.C) {
	cmd := cloud.NewSetDefaultCredentialCommand()
	_, err := testing.RunCommand(c, cmd)
	c.Assert(err, gc.ErrorMatches, "Usage: juju set-default-credential <cloud-name> <credential-name>")
	_, err = testing.RunCommand(c, cmd, "cloud", "credential", "extra")
	c.Assert(err, gc.ErrorMatches, `unrecognized args: \["extra"\]`)
}

func (s *defaultCredentialSuite) TestBadCredential(c *gc.C) {
	cmd := cloud.NewSetDefaultCredentialCommand()
	_, err := testing.RunCommand(c, cmd, "aws", "foo")
	c.Assert(err, gc.ErrorMatches, `credential "foo" for cloud aws not valid`)
}

func (s *defaultCredentialSuite) TestBadCloudName(c *gc.C) {
	cmd := cloud.NewSetDefaultRegionCommand()
	_, err := testing.RunCommand(c, cmd, "somecloud", "us-west-1")
	c.Assert(err, gc.ErrorMatches, `cloud somecloud not found`)
}

func (s *defaultCredentialSuite) TestSetDefaultCredential(c *gc.C) {
	store := jujuclienttesting.NewMemStore()
	store.Credentials["aws"] = jujucloud.CloudCredential{
		AuthCredentials: map[string]jujucloud.Credential{
			"my-sekrets": {},
		},
	}
	cmd := cloud.NewSetDefaultCredentialCommandForTest(store)
	ctx, err := testing.RunCommand(c, cmd, "aws", "my-sekrets")
	c.Assert(err, jc.ErrorIsNil)
	output := testing.Stderr(ctx)
	output = strings.Replace(output, "\n", "", -1)
	c.Assert(output, gc.Equals, `Default credential for aws set to "my-sekrets".`)
	c.Assert(store.Credentials["aws"].DefaultCredential, gc.Equals, "my-sekrets")
}
<commit_msg>fix test typo<commit_after>// Copyright 2016 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package cloud_test

import (
	"strings"

	jc "github.com/juju/testing/checkers"
	gc "gopkg.in/check.v1"

	jujucloud "github.com/juju/juju/cloud"
	"github.com/juju/juju/cmd/juju/cloud"
	"github.com/juju/juju/juju/osenv"
	"github.com/juju/juju/jujuclient/jujuclienttesting"
	"github.com/juju/juju/testing"
)

type defaultCredentialSuite struct {
	testing.BaseSuite
}

var _ = gc.Suite(&defaultCredentialSuite{})

func (s *defaultCredentialSuite) SetUpTest(c *gc.C) {
	origHome := osenv.SetJujuXDGDataHome(c.MkDir())
	s.AddCleanup(func(*gc.C) { osenv.SetJujuXDGDataHome(origHome) })
}

func (s *defaultCredentialSuite) TestBadArgs(c *gc.C) {
	cmd := cloud.NewSetDefaultCredentialCommand()
	_, err := testing.RunCommand(c, cmd)
	c.Assert(err, gc.ErrorMatches, "Usage: juju set-default-credential <cloud-name> <credential-name>")
	_, err = testing.RunCommand(c, cmd, "cloud", "credential", "extra")
	c.Assert(err, gc.ErrorMatches, `unrecognized args: \["extra"\]`)
}

func (s *defaultCredentialSuite) TestBadCredential(c *gc.C) {
	cmd := cloud.NewSetDefaultCredentialCommand()
	_, err := testing.RunCommand(c, cmd, "aws", "foo")
	c.Assert(err, gc.ErrorMatches, `credential "foo" for cloud aws not valid`)
}

func (s *defaultCredentialSuite) TestBadCloudName(c *gc.C) {
	cmd := cloud.NewSetDefaultCredentialCommand()
	_, err := testing.RunCommand(c, cmd, "somecloud", "us-west-1")
	c.Assert(err, gc.ErrorMatches, `cloud somecloud not found`)
}

func (s *defaultCredentialSuite) TestSetDefaultCredential(c *gc.C) {
	store := jujuclienttesting.NewMemStore()
	store.Credentials["aws"] = jujucloud.CloudCredential{
		AuthCredentials: map[string]jujucloud.Credential{
			"my-sekrets": {},
		},
	}
	cmd := cloud.NewSetDefaultCredentialCommandForTest(store)
	ctx, err := testing.RunCommand(c, cmd, "aws", "my-sekrets")
	c.Assert(err, jc.ErrorIsNil)
	output := testing.Stderr(ctx)
	output = strings.Replace(output, "\n", "", -1)
	c.Assert(output, gc.Equals, `Default credential for aws set to "my-sekrets".`)
	c.Assert(store.Credentials["aws"].DefaultCredential, gc.Equals, "my-sekrets")
}
<|endoftext|>


<commit_before>// +build !windows,!plan9

package tty

import (
	"os"
	"testing"
	"time"

	"github.com/u-root/u-root/cmds/elvish/edit/ui"
)

// timeout is the longest time the tests wait between writing something on
// the writer and reading it from the reader before declaring that the
// reader has a bug.
const timeoutInterval = 100 * time.Millisecond

func timeout() <-chan time.Time {
	return time.After(timeoutInterval)
}

var (
	theWriter   *os.File
	innerReader *os.File
	theReader   *reader
)

func TestMain(m *testing.M) {
	r, w, err := os.Pipe()
	if err != nil {
		panic("os.Pipe returned error, something is seriously wrong")
	}
	defer r.Close()
	defer w.Close()
	theWriter = w
	innerReader = r
	theReader = newReader(r)
	theReader.Start()
	defer theReader.Stop()

	os.Exit(m.Run())
}

var keyTests = []struct {
	input string
	want  Event
}{
	// Simple graphical key.
	{"x", KeyEvent{'x', 0}},
	{"X", KeyEvent{'X', 0}},
	{" ", KeyEvent{' ', 0}},

	// Ctrl key.
	{"\001", KeyEvent{'A', ui.Ctrl}},
	{"\033", KeyEvent{'[', ui.Ctrl}},

	// Ctrl-ish keys, but not thought as Ctrl keys by our reader.
	{"\n", KeyEvent{'\n', 0}},
	{"\t", KeyEvent{'\t', 0}},
	{"\x7f", KeyEvent{'\x7f', 0}}, // backspace

	// Alt plus simple graphical key.
	{"\033a", KeyEvent{'a', ui.Alt}},
	{"\033[", KeyEvent{'[', ui.Alt}},

	// G3-style key.
	{"\033OA", KeyEvent{ui.Up, 0}},
	{"\033OH", KeyEvent{ui.Home, 0}},

	// CSI-sequence key identified by the ending rune.
	{"\033[A", KeyEvent{ui.Up, 0}},
	{"\033[H", KeyEvent{ui.Home, 0}},
	// Test for all possible modifier
	{"\033[1;2A", KeyEvent{ui.Up, ui.Shift}},

	// CSI-sequence key with one argument, always ending in '~'.
	{"\033[1~", KeyEvent{ui.Home, 0}},
	{"\033[11~", KeyEvent{ui.F1, 0}},

	// CSI-sequence key with three arguments and ending in '~'. The first
	// argument is always 27, the second identifies the modifier and the last
	// identifies the key.
	{"\033[27;4;63~", KeyEvent{';', ui.Shift | ui.Alt}},
}

func TestKey(t *testing.T) {
	for _, test := range keyTests {
		theWriter.WriteString(test.input)
		select {
		case event := <-theReader.EventChan():
			if event != test.want {
				t.Errorf("Reader reads event %v, want %v", event, test.want)
			}
		case <-timeout():
			t.Errorf("Reader timed out")
		}
	}
}

// TestStopMakesUnderlyingFileAvailable tests that after calling Stop, the
// Reader no longer attempts to read from the underlying file, so it is
// available for use by others.
func TestStopMakesUnderlyingFileAvailable(t *testing.T) {
	theReader.Stop()
	defer theReader.Start()

	s := "lorem ipsum"
	theWriter.WriteString(s)
	gotChan := make(chan string)
	go func() {
		var buf [32]byte
		nr, err := innerReader.Read(buf[:])
		if err != nil {
			t.Errorf("inner.Read returns error: %v", err)
		}
		gotChan <- string(buf[:nr])
	}()
	select {
	case got := <-gotChan:
		if got != s {
			t.Errorf("got %q, want %q", got, s)
		}
	case <-time.After(time.Second):
		t.Error("inner.Read times out")
	}
}

// TestStartAfterStopIndeedStarts tests that calling Start very shortly after
// Stop puts the Reader the correct started state.
func TestStartAfterStopIndeedStarts(t *testing.T) {
	for i := 0; i < 100; i++ {
		theReader.Stop()
		theReader.Start()

		theWriter.WriteString("a")
		select {
		case event := <-theReader.EventChan():
			wantEvent := KeyEvent(ui.Key{'a', 0})
			if event != wantEvent {
				t.Errorf("After Stop and Start, Reader reads %v, want %v", event, wantEvent)
			}
		case <-timeout():
			t.Errorf("After Stop and Start, Reader timed out")
		}
	}
}
<commit_msg>Disable two flaky elvish tests<commit_after>// +build !windows,!plan9

package tty

import (
	"os"
	"testing"
	"time"

	"github.com/u-root/u-root/cmds/elvish/edit/ui"
)

// timeout is the longest time the tests wait between writing something on
// the writer and reading it from the reader before declaring that the
// reader has a bug.
const timeoutInterval = 100 * time.Millisecond

func timeout() <-chan time.Time {
	return time.After(timeoutInterval)
}

var (
	theWriter   *os.File
	innerReader *os.File
	theReader   *reader
)

func TestMain(m *testing.M) {
	r, w, err := os.Pipe()
	if err != nil {
		panic("os.Pipe returned error, something is seriously wrong")
	}
	defer r.Close()
	defer w.Close()
	theWriter = w
	innerReader = r
	theReader = newReader(r)
	theReader.Start()
	defer theReader.Stop()

	os.Exit(m.Run())
}

var keyTests = []struct {
	input string
	want  Event
}{
	// Simple graphical key.
	{"x", KeyEvent{'x', 0}},
	{"X", KeyEvent{'X', 0}},
	{" ", KeyEvent{' ', 0}},

	// Ctrl key.
	{"\001", KeyEvent{'A', ui.Ctrl}},
	{"\033", KeyEvent{'[', ui.Ctrl}},

	// Ctrl-ish keys, but not thought as Ctrl keys by our reader.
	{"\n", KeyEvent{'\n', 0}},
	{"\t", KeyEvent{'\t', 0}},
	{"\x7f", KeyEvent{'\x7f', 0}}, // backspace

	// Alt plus simple graphical key.
	{"\033a", KeyEvent{'a', ui.Alt}},
	{"\033[", KeyEvent{'[', ui.Alt}},

	// G3-style key.
	{"\033OA", KeyEvent{ui.Up, 0}},
	{"\033OH", KeyEvent{ui.Home, 0}},

	// CSI-sequence key identified by the ending rune.
	{"\033[A", KeyEvent{ui.Up, 0}},
	{"\033[H", KeyEvent{ui.Home, 0}},
	// Test for all possible modifier
	{"\033[1;2A", KeyEvent{ui.Up, ui.Shift}},

	// CSI-sequence key with one argument, always ending in '~'.
	{"\033[1~", KeyEvent{ui.Home, 0}},
	{"\033[11~", KeyEvent{ui.F1, 0}},

	// CSI-sequence key with three arguments and ending in '~'. The first
	// argument is always 27, the second identifies the modifier and the last
	// identifies the key.
	{"\033[27;4;63~", KeyEvent{';', ui.Shift | ui.Alt}},
}

// FIXME: Fix test flakiness.
func DISABLED_TestKey(t *testing.T) {
	for _, test := range keyTests {
		theWriter.WriteString(test.input)
		select {
		case event := <-theReader.EventChan():
			if event != test.want {
				t.Errorf("Reader reads event %v, want %v", event, test.want)
			}
		case <-timeout():
			t.Errorf("Reader timed out")
		}
	}
}

// TestStopMakesUnderlyingFileAvailable tests that after calling Stop, the
// Reader no longer attempts to read from the underlying file, so it is
// available for use by others.
func TestStopMakesUnderlyingFileAvailable(t *testing.T) {
	theReader.Stop()
	defer theReader.Start()

	s := "lorem ipsum"
	theWriter.WriteString(s)
	gotChan := make(chan string)
	go func() {
		var buf [32]byte
		nr, err := innerReader.Read(buf[:])
		if err != nil {
			t.Errorf("inner.Read returns error: %v", err)
		}
		gotChan <- string(buf[:nr])
	}()
	select {
	case got := <-gotChan:
		if got != s {
			t.Errorf("got %q, want %q", got, s)
		}
	case <-time.After(time.Second):
		t.Error("inner.Read times out")
	}
}

// TestStartAfterStopIndeedStarts tests that calling Start very shortly after
// Stop puts the Reader the correct started state.
// FIXME: Fix test flakiness.
func DISABLED_TestStartAfterStopIndeedStarts(t *testing.T) {
	for i := 0; i < 100; i++ {
		theReader.Stop()
		theReader.Start()

		theWriter.WriteString("a")
		select {
		case event := <-theReader.EventChan():
			wantEvent := KeyEvent(ui.Key{'a', 0})
			if event != wantEvent {
				t.Errorf("After Stop and Start, Reader reads %v, want %v", event, wantEvent)
			}
		case <-timeout():
			t.Errorf("After Stop and Start, Reader timed out")
		}
	}
}
<|endoftext|>


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

package simple_test

import (
	"fmt"

	"github.com/mediaFORGE/gol"
	mfmock "github.com/mediaFORGE/gol/internal/mock"
	logger_mock "github.com/mediaFORGE/gol/loggers/mock"
	logger_simple "github.com/mediaFORGE/gol/loggers/simple"
	"github.com/mediaFORGE/gol/manager/simple"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
	"github.com/stretchr/testify/suite"
)

const (
	// Capacity the number of messages the log message channel can hold.
	Capacity = 10
)

type ManagerTestSuite struct {
	suite.Suite
	manager gol.LoggerManager
}

func (s *ManagerTestSuite) testIsEnabled(n string, b bool, e error) {

	status, err := s.manager.IsEnabled(n)
	if e == nil {
		assert.Equal(s.T(), b, status)
		assert.Nil(s.T(), err)
	} else {
		assert.False(s.T(), status)
		assert.NotNil(s.T(), err)
	}
}

func (s *ManagerTestSuite) SetupTest() {
	s.manager = simple.New(Capacity)
}

func (s *ManagerTestSuite) TeardownTest() {
	s.manager.Close()
}

func (s *ManagerTestSuite) TestDeregister() {
	// setup
	l := logger_mock.New()
	s.manager.Register("mock", l)

	// deregister
	assert.Nil(s.T(), s.manager.Deregister("mock"))
	assert.Equal(s.T(), []string{}, s.manager.List())

	// inexistent
	assert.NotNil(s.T(), s.manager.Deregister("inexistent"))
}

func (s *ManagerTestSuite) TestDisable() {
	// setup
	l := logger_mock.New()
	s.manager.Register("mock", l)

	// disable
	assert.Nil(s.T(), s.manager.Disable("mock"))
	s.testIsEnabled("mock", false, nil)

	// inexistent
	assert.NotNil(s.T(), s.manager.Disable("inexistent"))
}

func (s *ManagerTestSuite) TestEnable() {
	// setup
	l := logger_mock.New()
	s.manager.Register("mock", l)

	// registered logger is enabled by default
	s.testIsEnabled("mock", true, nil)

	// enable a disabled logger
	s.manager.Disable("mock")
	assert.Nil(s.T(), s.manager.Enable("mock"))
	s.testIsEnabled("mock", true, nil)

	// inexistent
	assert.NotNil(s.T(), s.manager.Enable("inexistent"))
}

func (s *ManagerTestSuite) TestIsEnabled() {
	// setup
	l := logger_mock.New()
	s.manager.Register("mock", l)

	// enabled logger
	s.testIsEnabled("mock", true, nil)

	// disabled logger
	s.manager.Disable("mock")
	s.testIsEnabled("mock", false, nil)

	// inexistent logger
	s.testIsEnabled("inexistent", false, fmt.Errorf("error"))
}

func (s *ManagerTestSuite) TestList() {
	assert.Equal(s.T(), []string{}, s.manager.List())

	l := logger_mock.New()
	assert.Nil(s.T(), s.manager.Register("mock", l))
	assert.Equal(s.T(), []string{"mock"}, s.manager.List())
}

func (s *ManagerTestSuite) TestRegister() {
	l := logger_mock.New()
	assert.Nil(s.T(), s.manager.Register("mock", l))
	assert.Equal(s.T(), []string{"mock"}, s.manager.List())
	s.testIsEnabled("mock", true, nil)

	// duplicate
	assert.Nil(s.T(), s.manager.Register("mock", l))
	assert.Equal(s.T(), []string{"mock"}, s.manager.List())
	s.testIsEnabled("mock", true, nil)

	// nil
	assert.NotNil(s.T(), s.manager.Register("mock", nil))
}

func (s *ManagerTestSuite) TestSend() {
	m := gol.NewEmergency("field", "value")

	// l1 will not filter the message
	mf1 := &mfmock.LogFilter{}
	mf1.On("Filter", m).Return(false)
	mfmt1 := &mfmock.LogFormatter{}
	mfmt1.On("Format", m).Return("EMERGENCY field=value", nil)
	mw1 := &mfmock.Writer{}
	mw1.On("Write", mock.Anything).Return(21, nil)
	l1 := logger_simple.New(mf1, mfmt1, mw1)

	// l2 will filter the message
	mf2 := &mfmock.LogFilter{}
	mf2.On("Filter", m).Return(true)
	mfmt2 := &mfmock.LogFormatter{}
	mw2 := &mfmock.Writer{}
	l2 := logger_simple.New(mf2, mfmt2, mw2)

	s.manager.Register("l1", l1)
	s.manager.Register("l2", l2)

	s.manager.Run()
	assert.Nil(s.T(), s.manager.Send(m))
	s.manager.Close()

	mf1.AssertExpectations(s.T())
	mfmt1.AssertExpectations(s.T())
	mw1.AssertExpectations(s.T())

	mf2.AssertExpectations(s.T())
	mfmt2.AssertExpectations(s.T())
	mw2.AssertExpectations(s.T())
}

func (s *ManagerTestSuite) TestSendWithoutRun() {
	m := gol.NewEmergency("field", "value")

	assert.Equal(s.T(), s.manager.Send(m), fmt.Errorf("manager.simple.LogManager is not running"))
}
<commit_msg>added delay so test pass<commit_after>//
// Copyright 2015 Rakuten Marketing LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package simple_test

import (
	"fmt"
	"time"

	"github.com/mediaFORGE/gol"
	mfmock "github.com/mediaFORGE/gol/internal/mock"
	logger_mock "github.com/mediaFORGE/gol/loggers/mock"
	logger_simple "github.com/mediaFORGE/gol/loggers/simple"
	"github.com/mediaFORGE/gol/manager/simple"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
	"github.com/stretchr/testify/suite"
)

const (
	// Capacity the number of messages the log message channel can hold.
	Capacity = 1
)

type ManagerTestSuite struct {
	suite.Suite
	manager gol.LoggerManager
}

func (s *ManagerTestSuite) testIsEnabled(n string, b bool, e error) {

	status, err := s.manager.IsEnabled(n)
	if e == nil {
		assert.Equal(s.T(), b, status)
		assert.Nil(s.T(), err)
	} else {
		assert.False(s.T(), status)
		assert.NotNil(s.T(), err)
	}
}

func (s *ManagerTestSuite) SetupTest() {
	s.manager = simple.New(Capacity)
}

func (s *ManagerTestSuite) TeardownTest() {
	s.manager.Close()
}

func (s *ManagerTestSuite) TestDeregister() {
	// setup
	l := logger_mock.New()
	s.manager.Register("mock", l)

	// deregister
	assert.Nil(s.T(), s.manager.Deregister("mock"))
	assert.Equal(s.T(), []string{}, s.manager.List())

	// inexistent
	assert.NotNil(s.T(), s.manager.Deregister("inexistent"))
}

func (s *ManagerTestSuite) TestDisable() {
	// setup
	l := logger_mock.New()
	s.manager.Register("mock", l)

	// disable
	assert.Nil(s.T(), s.manager.Disable("mock"))
	s.testIsEnabled("mock", false, nil)

	// inexistent
	assert.NotNil(s.T(), s.manager.Disable("inexistent"))
}

func (s *ManagerTestSuite) TestEnable() {
	// setup
	l := logger_mock.New()
	s.manager.Register("mock", l)

	// registered logger is enabled by default
	s.testIsEnabled("mock", true, nil)

	// enable a disabled logger
	s.manager.Disable("mock")
	assert.Nil(s.T(), s.manager.Enable("mock"))
	s.testIsEnabled("mock", true, nil)

	// inexistent
	assert.NotNil(s.T(), s.manager.Enable("inexistent"))
}

func (s *ManagerTestSuite) TestIsEnabled() {
	// setup
	l := logger_mock.New()
	s.manager.Register("mock", l)

	// enabled logger
	s.testIsEnabled("mock", true, nil)

	// disabled logger
	s.manager.Disable("mock")
	s.testIsEnabled("mock", false, nil)

	// inexistent logger
	s.testIsEnabled("inexistent", false, fmt.Errorf("error"))
}

func (s *ManagerTestSuite) TestList() {
	assert.Equal(s.T(), []string{}, s.manager.List())

	l := logger_mock.New()
	assert.Nil(s.T(), s.manager.Register("mock", l))
	assert.Equal(s.T(), []string{"mock"}, s.manager.List())
}

func (s *ManagerTestSuite) TestRegister() {
	l := logger_mock.New()
	assert.Nil(s.T(), s.manager.Register("mock", l))
	assert.Equal(s.T(), []string{"mock"}, s.manager.List())
	s.testIsEnabled("mock", true, nil)

	// duplicate
	assert.Nil(s.T(), s.manager.Register("mock", l))
	assert.Equal(s.T(), []string{"mock"}, s.manager.List())
	s.testIsEnabled("mock", true, nil)

	// nil
	assert.NotNil(s.T(), s.manager.Register("mock", nil))
}

func (s *ManagerTestSuite) TestSend() {
	m := gol.NewEmergency("field", "value")

	// l1 will not filter the message
	mf1 := &mfmock.LogFilter{}
	mf1.On("Filter", m).Return(false)
	mfmt1 := &mfmock.LogFormatter{}
	mfmt1.On("Format", m).Return("EMERGENCY field=value", nil)
	mw1 := &mfmock.Writer{}
	mw1.On("Write", mock.Anything).Return(21, nil)
	l1 := logger_simple.New(mf1, mfmt1, mw1)

	// l2 will filter the message
	mf2 := &mfmock.LogFilter{}
	mf2.On("Filter", m).Return(true)
	mfmt2 := &mfmock.LogFormatter{}
	mw2 := &mfmock.Writer{}
	l2 := logger_simple.New(mf2, mfmt2, mw2)

	s.manager.Register("l1", l1)
	s.manager.Register("l2", l2)

	s.manager.Run()
	assert.Nil(s.T(), s.manager.Send(m))
	time.Sleep(1 * time.Second)
	s.manager.Close()

	mf1.AssertExpectations(s.T())
	mfmt1.AssertExpectations(s.T())
	mw1.AssertExpectations(s.T())

	mf2.AssertExpectations(s.T())
	mfmt2.AssertExpectations(s.T())
	mw2.AssertExpectations(s.T())
}

func (s *ManagerTestSuite) TestSendWithoutRun() {
	m := gol.NewEmergency("field", "value")

	assert.Equal(s.T(), s.manager.Send(m), fmt.Errorf("manager.simple.LogManager is not running"))
}
<|endoftext|>