<commit_before>package indicators_test

import (
	. "github.com/onsi/ginkgo"
	"github.com/thetruetrade/gotrade/indicators"
)

var _ = Describe("when calculating an median price (medprice) with DOHLCV source data", func() {
	var (
		indicator *indicators.MedPrice
		inputs    IndicatorWithFloatBoundsSharedSpecInputs
	)

	BeforeEach(func() {
		indicator, _ = indicators.NewMedPrice()

		inputs = NewIndicatorWithFloatBoundsSharedSpecInputs(indicator, len(sourceDOHLCVData), indicator,
			func() float64 {
				return GetFloatDataMax(indicator.Data)
			},
			func() float64 {
				return GetFloatDataMin(indicator.Data)
			})
	})

	Context("and the indicator has not yet received any ticks", func() {
		ShouldBeAnInitialisedIndicator(&inputs)

		ShouldNotHaveAnyFloatBoundsSetYet(&inputs)
	})

	Context("and the indicator has received less ticks than the lookback period", func() {

		BeforeEach(func() {
			for i := 0; i < indicator.GetLookbackPeriod(); i++ {
				indicator.ReceiveDOHLCVTick(sourceDOHLCVData[i], i+1)
			}
		})

		ShouldBeAnIndicatorThatHasReceivedFewerTicksThanItsLookbackPeriod(&inputs)

		ShouldNotHaveAnyFloatBoundsSetYet(&inputs)
	})

	Context("and the indicator has received ticks equal to the lookback period", func() {

		BeforeEach(func() {
			for i := 0; i <= indicator.GetLookbackPeriod(); i++ {
				indicator.ReceiveDOHLCVTick(sourceDOHLCVData[i], i+1)
			}
		})

		ShouldBeAnIndicatorThatHasReceivedTicksEqualToItsLookbackPeriod(&inputs)

		ShouldHaveFloatBoundsSetToMinMaxOfResults(&inputs)
	})

	Context("and the indicator has received more ticks than the lookback period", func() {

		BeforeEach(func() {
			for i := range sourceDOHLCVData {
				indicator.ReceiveDOHLCVTick(sourceDOHLCVData[i], i+1)
			}
		})

		ShouldBeAnIndicatorThatHasReceivedMoreTicksThanItsLookbackPeriod(&inputs)

		ShouldHaveFloatBoundsSetToMinMaxOfResults(&inputs)
	})

	Context("and the indicator has recieved all of its ticks", func() {
		BeforeEach(func() {
			for i := 0; i < len(sourceDOHLCVData); i++ {
				indicator.ReceiveDOHLCVTick(sourceDOHLCVData[i], i+1)
			}
		})

		ShouldBeAnIndicatorThatHasReceivedAllOfItsTicks(&inputs)

		ShouldHaveFloatBoundsSetToMinMaxOfResults(&inputs)
	})
})
<commit_msg>#77 achieve 100% test coverage for indicators - medianprice<commit_after>package indicators_test

import (
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/thetruetrade/gotrade/indicators"
)

var _ = Describe("when creating an medpricewithoutstorage", func() {
	var (
		indicator      *indicators.MedPriceWithoutStorage
		indicatorError error
	)

	Context("and the indicator was not given a value available action", func() {
		BeforeEach(func() {
			indicator, indicatorError = indicators.NewMedPriceWithoutStorage(nil)
		})

		It("the indicator should not be created and return the appropriate error message", func() {
			Expect(indicator).To(BeNil())
			Expect(indicatorError).To(Equal(indicators.ErrValueAvailableActionIsNil))
		})
	})
})

var _ = Describe("when calculating an median price (medprice) with DOHLCV source data", func() {
	var (
		indicator *indicators.MedPrice
		inputs    IndicatorWithFloatBoundsSharedSpecInputs
		stream    *fakeDOHLCVStreamSubscriber
	)

	Context("given the indicator is created via the standard constructor", func() {
		BeforeEach(func() {
			indicator, _ = indicators.NewMedPrice()

			inputs = NewIndicatorWithFloatBoundsSharedSpecInputs(indicator, len(sourceDOHLCVData), indicator,
				func() float64 {
					return GetFloatDataMax(indicator.Data)
				},
				func() float64 {
					return GetFloatDataMin(indicator.Data)
				})
		})

		Context("and the indicator has not yet received any ticks", func() {
			ShouldBeAnInitialisedIndicator(&inputs)

			ShouldNotHaveAnyFloatBoundsSetYet(&inputs)
		})

		Context("and the indicator has received less ticks than the lookback period", func() {

			BeforeEach(func() {
				for i := 0; i < indicator.GetLookbackPeriod(); i++ {
					indicator.ReceiveDOHLCVTick(sourceDOHLCVData[i], i+1)
				}
			})

			ShouldBeAnIndicatorThatHasReceivedFewerTicksThanItsLookbackPeriod(&inputs)

			ShouldNotHaveAnyFloatBoundsSetYet(&inputs)
		})

		Context("and the indicator has received ticks equal to the lookback period", func() {

			BeforeEach(func() {
				for i := 0; i <= indicator.GetLookbackPeriod(); i++ {
					indicator.ReceiveDOHLCVTick(sourceDOHLCVData[i], i+1)
				}
			})

			ShouldBeAnIndicatorThatHasReceivedTicksEqualToItsLookbackPeriod(&inputs)

			ShouldHaveFloatBoundsSetToMinMaxOfResults(&inputs)
		})

		Context("and the indicator has received more ticks than the lookback period", func() {

			BeforeEach(func() {
				for i := range sourceDOHLCVData {
					indicator.ReceiveDOHLCVTick(sourceDOHLCVData[i], i+1)
				}
			})

			ShouldBeAnIndicatorThatHasReceivedMoreTicksThanItsLookbackPeriod(&inputs)

			ShouldHaveFloatBoundsSetToMinMaxOfResults(&inputs)
		})

		Context("and the indicator has recieved all of its ticks", func() {
			BeforeEach(func() {
				for i := 0; i < len(sourceDOHLCVData); i++ {
					indicator.ReceiveDOHLCVTick(sourceDOHLCVData[i], i+1)
				}
			})

			ShouldBeAnIndicatorThatHasReceivedAllOfItsTicks(&inputs)

			ShouldHaveFloatBoundsSetToMinMaxOfResults(&inputs)
		})
	})

	Context("given the indicator is created via the constructor with defaulted parameters", func() {
		BeforeEach(func() {
			indicator, _ = indicators.NewDefaultMedPrice()
			inputs = NewIndicatorWithFloatBoundsSharedSpecInputs(indicator, len(sourceDOHLCVData), indicator,
				func() float64 {
					return GetFloatDataMax(indicator.Data)
				},
				func() float64 {
					return GetFloatDataMin(indicator.Data)
				})
		})

		Context("and the indicator has not yet received any ticks", func() {
			ShouldBeAnInitialisedIndicator(&inputs)

			ShouldNotHaveAnyFloatBoundsSetYet(&inputs)
		})

		Context("and the indicator has recieved all of its ticks", func() {
			BeforeEach(func() {
				for i := 0; i < len(sourceDOHLCVData); i++ {
					indicator.ReceiveDOHLCVTick(sourceDOHLCVData[i], i+1)
				}
			})

			ShouldBeAnIndicatorThatHasReceivedAllOfItsTicks(&inputs)

			ShouldHaveFloatBoundsSetToMinMaxOfResults(&inputs)
		})
	})

	Context("given the indicator is created via the constructor with fixed source length", func() {
		BeforeEach(func() {
			indicator, _ = indicators.NewMedPriceWithSrcLen(uint(len(sourceDOHLCVData)))
			inputs = NewIndicatorWithFloatBoundsSharedSpecInputs(indicator, len(sourceDOHLCVData), indicator,
				func() float64 {
					return GetFloatDataMax(indicator.Data)
				},
				func() float64 {
					return GetFloatDataMin(indicator.Data)
				})
		})

		It("should have pre-allocated storge for the output data", func() {
			Expect(cap(indicator.Data)).To(Equal(len(sourceDOHLCVData) - indicator.GetLookbackPeriod()))
		})

		Context("and the indicator has not yet received any ticks", func() {
			ShouldBeAnInitialisedIndicator(&inputs)

			ShouldNotHaveAnyFloatBoundsSetYet(&inputs)
		})

		Context("and the indicator has recieved all of its ticks", func() {
			BeforeEach(func() {
				for i := 0; i < len(sourceDOHLCVData); i++ {
					indicator.ReceiveDOHLCVTick(sourceDOHLCVData[i], i+1)
				}
			})

			ShouldBeAnIndicatorThatHasReceivedAllOfItsTicks(&inputs)

			ShouldHaveFloatBoundsSetToMinMaxOfResults(&inputs)

			It("no new storage capcity should have been allocated", func() {
				Expect(len(indicator.Data)).To(Equal(cap(indicator.Data)))
			})
		})
	})

	Context("given the indicator is created via the constructor with defaulted parameters and fixed source length", func() {
		BeforeEach(func() {
			indicator, _ = indicators.NewDefaultMedPriceWithSrcLen(uint(len(sourceDOHLCVData)))
			inputs = NewIndicatorWithFloatBoundsSharedSpecInputs(indicator, len(sourceDOHLCVData), indicator,
				func() float64 {
					return GetFloatDataMax(indicator.Data)
				},
				func() float64 {
					return GetFloatDataMin(indicator.Data)
				})
		})

		It("should have pre-allocated storge for the output data", func() {
			Expect(cap(indicator.Data)).To(Equal(len(sourceDOHLCVData) - indicator.GetLookbackPeriod()))
		})

		Context("and the indicator has not yet received any ticks", func() {
			ShouldBeAnInitialisedIndicator(&inputs)

			ShouldNotHaveAnyFloatBoundsSetYet(&inputs)
		})

		Context("and the indicator has recieved all of its ticks", func() {
			BeforeEach(func() {
				for i := 0; i < len(sourceDOHLCVData); i++ {
					indicator.ReceiveDOHLCVTick(sourceDOHLCVData[i], i+1)
				}
			})

			ShouldBeAnIndicatorThatHasReceivedAllOfItsTicks(&inputs)

			ShouldHaveFloatBoundsSetToMinMaxOfResults(&inputs)

			It("no new storage capcity should have been allocated", func() {
				Expect(len(indicator.Data)).To(Equal(cap(indicator.Data)))
			})
		})
	})

	Context("given the indicator is created via the constructor for use with a price stream", func() {
		BeforeEach(func() {
			stream = newFakeDOHLCVStreamSubscriber()
			indicator, _ = indicators.NewMedPriceForStream(stream)
			inputs = NewIndicatorWithFloatBoundsSharedSpecInputs(indicator, len(sourceDOHLCVData), indicator,
				func() float64 {
					return GetFloatDataMax(indicator.Data)
				},
				func() float64 {
					return GetFloatDataMin(indicator.Data)
				})
		})

		It("should have requested to be attached to the stream", func() {
			Expect(stream.lastCallToAddTickSubscriptionArg).To(Equal(indicator))
		})

		Context("and the indicator has not yet received any ticks", func() {
			ShouldBeAnInitialisedIndicator(&inputs)

			ShouldNotHaveAnyFloatBoundsSetYet(&inputs)
		})

		Context("and the indicator has recieved all of its ticks", func() {
			BeforeEach(func() {
				for i := 0; i < len(sourceDOHLCVData); i++ {
					indicator.ReceiveDOHLCVTick(sourceDOHLCVData[i], i+1)
				}
			})

			ShouldBeAnIndicatorThatHasReceivedAllOfItsTicks(&inputs)

			ShouldHaveFloatBoundsSetToMinMaxOfResults(&inputs)
		})
	})

	Context("given the indicator is created via the constructor for use with a price stream with defaulted parameters", func() {
		BeforeEach(func() {
			stream = newFakeDOHLCVStreamSubscriber()
			indicator, _ = indicators.NewDefaultMedPriceForStream(stream)
			inputs = NewIndicatorWithFloatBoundsSharedSpecInputs(indicator, len(sourceDOHLCVData), indicator,
				func() float64 {
					return GetFloatDataMax(indicator.Data)
				},
				func() float64 {
					return GetFloatDataMin(indicator.Data)
				})
		})

		It("should have requested to be attached to the stream", func() {
			Expect(stream.lastCallToAddTickSubscriptionArg).To(Equal(indicator))
		})

		Context("and the indicator has not yet received any ticks", func() {
			ShouldBeAnInitialisedIndicator(&inputs)

			ShouldNotHaveAnyFloatBoundsSetYet(&inputs)
		})

		Context("and the indicator has recieved all of its ticks", func() {
			BeforeEach(func() {
				for i := 0; i < len(sourceDOHLCVData); i++ {
					indicator.ReceiveDOHLCVTick(sourceDOHLCVData[i], i+1)
				}
			})

			ShouldBeAnIndicatorThatHasReceivedAllOfItsTicks(&inputs)

			ShouldHaveFloatBoundsSetToMinMaxOfResults(&inputs)
		})
	})

	Context("given the indicator is created via the constructor for use with a price stream with fixed source length", func() {
		BeforeEach(func() {
			stream = newFakeDOHLCVStreamSubscriber()
			indicator, _ = indicators.NewMedPriceForStreamWithSrcLen(uint(len(sourceDOHLCVData)), stream)
			inputs = NewIndicatorWithFloatBoundsSharedSpecInputs(indicator, len(sourceDOHLCVData), indicator,
				func() float64 {
					return GetFloatDataMax(indicator.Data)
				},
				func() float64 {
					return GetFloatDataMin(indicator.Data)
				})
		})

		It("should have pre-allocated storge for the output data", func() {
			Expect(cap(indicator.Data)).To(Equal(len(sourceDOHLCVData) - indicator.GetLookbackPeriod()))
		})

		It("should have requested to be attached to the stream", func() {
			Expect(stream.lastCallToAddTickSubscriptionArg).To(Equal(indicator))
		})

		Context("and the indicator has not yet received any ticks", func() {
			ShouldBeAnInitialisedIndicator(&inputs)

			ShouldNotHaveAnyFloatBoundsSetYet(&inputs)
		})

		Context("and the indicator has recieved all of its ticks", func() {
			BeforeEach(func() {
				for i := 0; i < len(sourceDOHLCVData); i++ {
					indicator.ReceiveDOHLCVTick(sourceDOHLCVData[i], i+1)
				}
			})

			ShouldBeAnIndicatorThatHasReceivedAllOfItsTicks(&inputs)

			ShouldHaveFloatBoundsSetToMinMaxOfResults(&inputs)

			It("no new storage capcity should have been allocated", func() {
				Expect(len(indicator.Data)).To(Equal(cap(indicator.Data)))
			})
		})
	})

	Context("given the indicator is created via the constructor for use with a price stream with fixed source length with defaulted parmeters", func() {
		BeforeEach(func() {
			stream = newFakeDOHLCVStreamSubscriber()
			indicator, _ = indicators.NewDefaultMedPriceForStreamWithSrcLen(uint(len(sourceDOHLCVData)), stream)
			inputs = NewIndicatorWithFloatBoundsSharedSpecInputs(indicator, len(sourceDOHLCVData), indicator,
				func() float64 {
					return GetFloatDataMax(indicator.Data)
				},
				func() float64 {
					return GetFloatDataMin(indicator.Data)
				})
		})

		It("should have pre-allocated storge for the output data", func() {
			Expect(cap(indicator.Data)).To(Equal(len(sourceDOHLCVData) - indicator.GetLookbackPeriod()))
		})

		It("should have requested to be attached to the stream", func() {
			Expect(stream.lastCallToAddTickSubscriptionArg).To(Equal(indicator))
		})

		Context("and the indicator has not yet received any ticks", func() {
			ShouldBeAnInitialisedIndicator(&inputs)

			ShouldNotHaveAnyFloatBoundsSetYet(&inputs)
		})

		Context("and the indicator has recieved all of its ticks", func() {
			BeforeEach(func() {
				for i := 0; i < len(sourceDOHLCVData); i++ {
					indicator.ReceiveDOHLCVTick(sourceDOHLCVData[i], i+1)
				}
			})

			ShouldBeAnIndicatorThatHasReceivedAllOfItsTicks(&inputs)

			ShouldHaveFloatBoundsSetToMinMaxOfResults(&inputs)

			It("no new storage capcity should have been allocated", func() {
				Expect(len(indicator.Data)).To(Equal(cap(indicator.Data)))
			})
		})
	})

})
<|endoftext|>


<commit_before>package inputs_test

import (
	"testing"
	"time"

	"github.com/influxdata/flux/functions/inputs"
	"github.com/influxdata/flux/functions/transformations"

	"github.com/influxdata/flux"
	"github.com/influxdata/flux/execute"
	"github.com/influxdata/flux/querytest"
	"github.com/influxdata/platform"
	pquerytest "github.com/influxdata/platform/query/querytest"
)

func TestFrom_NewQuery(t *testing.T) {
	t.Skip()
	tests := []querytest.NewQueryTestCase{
		{
			Name:    "from no args",
			Raw:     `from()`,
			WantErr: true,
		},
		{
			Name:    "from conflicting args",
			Raw:     `from(bucket:"d", bucket:"b")`,
			WantErr: true,
		},
		{
			Name:    "from repeat arg",
			Raw:     `from(bucket:"telegraf", bucket:"oops")`,
			WantErr: true,
		},
		{
			Name:    "from",
			Raw:     `from(bucket:"telegraf", chicken:"what is this?")`,
			WantErr: true,
		},
		{
			Name:    "from bucket invalid ID",
			Raw:     `from(bucketID:"invalid")`,
			WantErr: true,
		},
		{
			Name: "from bucket ID",
			Raw:  `from(bucketID:"aaaaaaaa")`,
			Want: &flux.Spec{
				Operations: []*flux.Operation{
					{
						ID: "from0",
						Spec: &inputs.FromOpSpec{
							BucketID: "aaaaaaaa",
						},
					},
				},
			},
		},
		{
			Name: "from with database",
			Raw:  `from(bucket:"mybucket") |> range(start:-4h, stop:-2h) |> sum()`,
			Want: &flux.Spec{
				Operations: []*flux.Operation{
					{
						ID: "from0",
						Spec: &inputs.FromOpSpec{
							Bucket: "mybucket",
						},
					},
					{
						ID: "range1",
						Spec: &transformations.RangeOpSpec{
							Start: flux.Time{
								Relative:   -4 * time.Hour,
								IsRelative: true,
							},
							Stop: flux.Time{
								Relative:   -2 * time.Hour,
								IsRelative: true,
							},
							TimeCol:  "_time",
							StartCol: "_start",
							StopCol:  "_stop",
						},
					},
					{
						ID: "sum2",
						Spec: &transformations.SumOpSpec{
							AggregateConfig: execute.DefaultAggregateConfig,
						},
					},
				},
				Edges: []flux.Edge{
					{Parent: "from0", Child: "range1"},
					{Parent: "range1", Child: "sum2"},
				},
			},
		},
	}
	for _, tc := range tests {
		tc := tc
		t.Run(tc.Name, func(t *testing.T) {
			t.Parallel()
			querytest.NewQueryTestHelper(t, tc)
		})
	}
}

func TestFromOperation_Marshaling(t *testing.T) {
	t.Skip()
	data := []byte(`{"id":"from","kind":"from","spec":{"bucket":"mybucket"}}`)
	op := &flux.Operation{
		ID: "from",
		Spec: &inputs.FromOpSpec{
			Bucket: "mybucket",
		},
	}
	querytest.OperationMarshalingTestHelper(t, data, op)
}

func TestFromOpSpec_BucketsAccessed(t *testing.T) {
	// TODO(adam) add this test back when BucketsAccessed is restored for the from function
	// https://github.com/influxdata/flux/issues/114
	t.Skip("https://github.com/influxdata/flux/issues/114")
	bucketName := "my_bucket"
	bucketID, _ := platform.IDFromString("deadbeef")
	tests := []pquerytest.BucketAwareQueryTestCase{
		{
			Name:             "From with bucket",
			Raw:              `from(bucket:"my_bucket")`,
			WantReadBuckets:  &[]platform.BucketFilter{{Name: &bucketName}},
			WantWriteBuckets: &[]platform.BucketFilter{},
		},
		{
			Name:             "From with bucketID",
			Raw:              `from(bucketID:"deadbeef")`,
			WantReadBuckets:  &[]platform.BucketFilter{{ID: bucketID}},
			WantWriteBuckets: &[]platform.BucketFilter{},
		},
	}
	for _, tc := range tests {
		tc := tc
		t.Run(tc.Name, func(t *testing.T) {
			t.Parallel()
			pquerytest.BucketAwareQueryTestHelper(t, tc)
		})
	}
}
<commit_msg>fix(functions/input): use 16 character hex string for test<commit_after>package inputs_test

import (
	"testing"
	"time"

	"github.com/influxdata/flux/functions/inputs"
	"github.com/influxdata/flux/functions/transformations"

	"github.com/influxdata/flux"
	"github.com/influxdata/flux/execute"
	"github.com/influxdata/flux/querytest"
	"github.com/influxdata/platform"
	pquerytest "github.com/influxdata/platform/query/querytest"
)

func TestFrom_NewQuery(t *testing.T) {
	t.Skip()
	tests := []querytest.NewQueryTestCase{
		{
			Name:    "from no args",
			Raw:     `from()`,
			WantErr: true,
		},
		{
			Name:    "from conflicting args",
			Raw:     `from(bucket:"d", bucket:"b")`,
			WantErr: true,
		},
		{
			Name:    "from repeat arg",
			Raw:     `from(bucket:"telegraf", bucket:"oops")`,
			WantErr: true,
		},
		{
			Name:    "from",
			Raw:     `from(bucket:"telegraf", chicken:"what is this?")`,
			WantErr: true,
		},
		{
			Name:    "from bucket invalid ID",
			Raw:     `from(bucketID:"invalid")`,
			WantErr: true,
		},
		{
			Name: "from bucket ID",
			Raw:  `from(bucketID:"aaaabbbbccccdddd")`,
			Want: &flux.Spec{
				Operations: []*flux.Operation{
					{
						ID: "from0",
						Spec: &inputs.FromOpSpec{
							BucketID: "aaaabbbbccccdddd",
						},
					},
				},
			},
		},
		{
			Name: "from with database",
			Raw:  `from(bucket:"mybucket") |> range(start:-4h, stop:-2h) |> sum()`,
			Want: &flux.Spec{
				Operations: []*flux.Operation{
					{
						ID: "from0",
						Spec: &inputs.FromOpSpec{
							Bucket: "mybucket",
						},
					},
					{
						ID: "range1",
						Spec: &transformations.RangeOpSpec{
							Start: flux.Time{
								Relative:   -4 * time.Hour,
								IsRelative: true,
							},
							Stop: flux.Time{
								Relative:   -2 * time.Hour,
								IsRelative: true,
							},
							TimeCol:  "_time",
							StartCol: "_start",
							StopCol:  "_stop",
						},
					},
					{
						ID: "sum2",
						Spec: &transformations.SumOpSpec{
							AggregateConfig: execute.DefaultAggregateConfig,
						},
					},
				},
				Edges: []flux.Edge{
					{Parent: "from0", Child: "range1"},
					{Parent: "range1", Child: "sum2"},
				},
			},
		},
	}
	for _, tc := range tests {
		tc := tc
		t.Run(tc.Name, func(t *testing.T) {
			t.Parallel()
			querytest.NewQueryTestHelper(t, tc)
		})
	}
}

func TestFromOperation_Marshaling(t *testing.T) {
	t.Skip()
	data := []byte(`{"id":"from","kind":"from","spec":{"bucket":"mybucket"}}`)
	op := &flux.Operation{
		ID: "from",
		Spec: &inputs.FromOpSpec{
			Bucket: "mybucket",
		},
	}
	querytest.OperationMarshalingTestHelper(t, data, op)
}

func TestFromOpSpec_BucketsAccessed(t *testing.T) {
	// TODO(adam) add this test back when BucketsAccessed is restored for the from function
	// https://github.com/influxdata/flux/issues/114
	t.Skip("https://github.com/influxdata/flux/issues/114")
	bucketName := "my_bucket"
	bucketID, _ := platform.IDFromString("deadbeef")
	tests := []pquerytest.BucketAwareQueryTestCase{
		{
			Name:             "From with bucket",
			Raw:              `from(bucket:"my_bucket")`,
			WantReadBuckets:  &[]platform.BucketFilter{{Name: &bucketName}},
			WantWriteBuckets: &[]platform.BucketFilter{},
		},
		{
			Name:             "From with bucketID",
			Raw:              `from(bucketID:"deadbeef")`,
			WantReadBuckets:  &[]platform.BucketFilter{{ID: bucketID}},
			WantWriteBuckets: &[]platform.BucketFilter{},
		},
	}
	for _, tc := range tests {
		tc := tc
		t.Run(tc.Name, func(t *testing.T) {
			t.Parallel()
			pquerytest.BucketAwareQueryTestHelper(t, tc)
		})
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 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.

package context

import (
	"bytes"
	"os"
	"path/filepath"
	"strings"

	"github.com/kardianos/govendor/internal/github.com/dchest/safefile"
	"github.com/kardianos/govendor/internal/pathos"
	"github.com/kardianos/govendor/vendorfile"
)

// WriteVendorFile writes the current vendor file to the context location.
func (ctx *Context) WriteVendorFile() (err error) {
	perm := os.FileMode(0777)
	fi, err := os.Stat(ctx.VendorFilePath)
	if err == nil {
		perm = fi.Mode()
	}

	buf := &bytes.Buffer{}
	err = ctx.VendorFile.Marshal(buf)
	if err != nil {
		return
	}
	dir, _ := filepath.Split(ctx.VendorFilePath)
	err = os.MkdirAll(dir, 0777)
	if err != nil {
		return
	}
	err = safefile.WriteFile(ctx.VendorFilePath, buf.Bytes(), perm)
	return
}

func readVendorFile(vendorFilePath string) (*vendorfile.File, error) {
	vf := &vendorfile.File{}
	f, err := os.Open(vendorFilePath)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	err = vf.Unmarshal(f)
	if err != nil {
		return nil, err
	}
	// Determine if local field is relative to GOPATH or vendor file.
	// Change to relative to vendor file as needed.
	folder, _ := filepath.Split(vendorFilePath)
	relToFile := 0
	relToGOPATH := 0
	for _, pkg := range vf.Package {
		p := filepath.Join(folder, pathos.SlashToFilepath(pkg.Local))
		_, err := os.Stat(p)
		if os.IsNotExist(err) {
			relToGOPATH++
			continue
		}
		relToFile++
	}
	if relToFile > relToGOPATH || len(vf.Package) == 0 {
		return vf, nil
	}

	gopathList := strings.Split(os.Getenv("GOPATH"), string(os.PathListSeparator))
	gopath := ""
	for _, gp := range gopathList {
		if pathos.FileHasPrefix(folder, gp) {
			gopath = gp
			break
		}
	}
	if len(gopath) == 0 {
		return vf, nil
	}
	prefix := pathos.FileTrimPrefix(folder, filepath.Join(gopath, "src"))
	if len(prefix) > 0 {
		prefix = prefix[1:]
	}
	for _, pkg := range vf.Package {
		pkg.Local = strings.TrimPrefix(pkg.Local, prefix)
	}

	return vf, nil
}
<commit_msg>context: convert filesystem path to import path.<commit_after>// Copyright 2015 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.

package context

import (
	"bytes"
	"os"
	"path/filepath"
	"strings"

	"github.com/kardianos/govendor/internal/github.com/dchest/safefile"
	"github.com/kardianos/govendor/internal/pathos"
	"github.com/kardianos/govendor/vendorfile"
)

// WriteVendorFile writes the current vendor file to the context location.
func (ctx *Context) WriteVendorFile() (err error) {
	perm := os.FileMode(0777)
	fi, err := os.Stat(ctx.VendorFilePath)
	if err == nil {
		perm = fi.Mode()
	}

	buf := &bytes.Buffer{}
	err = ctx.VendorFile.Marshal(buf)
	if err != nil {
		return
	}
	dir, _ := filepath.Split(ctx.VendorFilePath)
	err = os.MkdirAll(dir, 0777)
	if err != nil {
		return
	}
	err = safefile.WriteFile(ctx.VendorFilePath, buf.Bytes(), perm)
	return
}

func readVendorFile(vendorFilePath string) (*vendorfile.File, error) {
	vf := &vendorfile.File{}
	f, err := os.Open(vendorFilePath)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	err = vf.Unmarshal(f)
	if err != nil {
		return nil, err
	}
	// Determine if local field is relative to GOPATH or vendor file.
	// Change to relative to vendor file as needed.
	folder, _ := filepath.Split(vendorFilePath)
	relToFile := 0
	relToGOPATH := 0
	for _, pkg := range vf.Package {
		p := filepath.Join(folder, pathos.SlashToFilepath(pkg.Local))
		_, err := os.Stat(p)
		if os.IsNotExist(err) {
			relToGOPATH++
			continue
		}
		relToFile++
	}
	if relToFile > relToGOPATH || len(vf.Package) == 0 {
		return vf, nil
	}

	gopathList := strings.Split(os.Getenv("GOPATH"), string(os.PathListSeparator))
	gopath := ""
	for _, gp := range gopathList {
		if pathos.FileHasPrefix(folder, gp) {
			gopath = gp
			break
		}
	}
	if len(gopath) == 0 {
		return vf, nil
	}
	prefix := pathos.SlashToImportPath(pathos.FileTrimPrefix(folder, filepath.Join(gopath, "src")))
	prefix = strings.TrimPrefix(prefix, "/")
	for _, pkg := range vf.Package {
		pkg.Local = strings.TrimPrefix(pkg.Local, prefix)
	}

	return vf, nil
}
<|endoftext|>


<commit_before>// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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 iid contains integration tests for the firebase.google.com/go/iid package.
package iid

import (
	"flag"
	"log"
	"os"
	"testing"

	"firebase.google.com/go/iid"
	"firebase.google.com/go/integration/internal"

	"golang.org/x/net/context"
)

var client *iid.Client

func TestMain(m *testing.M) {
	flag.Parse()
	if testing.Short() {
		log.Println("skipping instance ID integration tests in short mode.")
		os.Exit(0)
	}

	ctx := context.Background()
	app, err := internal.NewTestApp(ctx)
	if err != nil {
		log.Fatalln(err)
	}

	client, err = app.InstanceID(ctx)
	if err != nil {
		log.Fatalln(err)
	}

	os.Exit(m.Run())
}

func TestNonExisting(t *testing.T) {
	err := client.DeleteInstanceID(context.Background(), "dnon-existY")
	if err == nil {
		t.Errorf("DeleteInstanceID(\"dnon-existY\") = nil; want error")
	}
	want := `instance id "dnon-existY": failed to find the instance id`
	if err.Error() != want {
		t.Errorf("DeleteInstanceID(\"dnon-existY\") = %v; want = %v", err, want)
	}
}
<commit_msg>Document non existing name in integration tests for iid (#85)<commit_after>// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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 iid contains integration tests for the firebase.google.com/go/iid package.
package iid

import (
	"flag"
	"log"
	"os"
	"testing"

	"firebase.google.com/go/iid"
	"firebase.google.com/go/integration/internal"

	"golang.org/x/net/context"
)

var client *iid.Client

func TestMain(m *testing.M) {
	flag.Parse()
	if testing.Short() {
		log.Println("skipping instance ID integration tests in short mode.")
		os.Exit(0)
	}

	ctx := context.Background()
	app, err := internal.NewTestApp(ctx)
	if err != nil {
		log.Fatalln(err)
	}

	client, err = app.InstanceID(ctx)
	if err != nil {
		log.Fatalln(err)
	}

	os.Exit(m.Run())
}

func TestNonExisting(t *testing.T) {
	// legal instance IDs are /[cdef][A-Za-z0-9_-]{9}[AEIMQUYcgkosw048]/
	err := client.DeleteInstanceID(context.Background(), "fictive-ID0")
	if err == nil {
		t.Errorf("DeleteInstanceID(non-existing) = nil; want error")
	}
	want := `instance id "fictive-ID0": failed to find the instance id`
	if err.Error() != want {
		t.Errorf("DeleteInstanceID(non-existing) = %v; want = %v", err, want)
	}
}
<|endoftext|>


<commit_before>package console

import (
	"errors"
	"fmt"
	"sort"
	"sync"
	"time"

	"github.com/cenkalti/rain/rainrpc"
	"github.com/cenkalti/rain/torrent"
	"github.com/hokaccha/go-prettyjson"
	"github.com/jroimartin/gocui"
)

type Console struct {
	client       *rainrpc.Client
	torrents     []rainrpc.Torrent
	errTorrents  error
	selectedID   uint64
	stats        torrent.Stats
	errStats     error
	m            sync.Mutex
	updateStatsC chan struct{}
}

func New(clt *rainrpc.Client) *Console {
	return &Console{
		client:       clt,
		updateStatsC: make(chan struct{}),
	}
}

func (c *Console) Run() error {
	g, err := gocui.NewGui(gocui.OutputNormal)
	if err != nil {
		return err
	}
	defer g.Close()

	g.SetManagerFunc(c.layout)

	g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit)
	g.SetKeybinding("", 'q', gocui.ModNone, quit)
	g.SetKeybinding("torrents", 'j', gocui.ModNone, c.cursorDown)
	g.SetKeybinding("torrents", 'k', gocui.ModNone, c.cursorUp)
	g.SetKeybinding("torrents", 'R', gocui.ModNone, c.removeTorrent)

	go c.updateLoop(g)

	err = g.MainLoop()
	if err == gocui.ErrQuit {
		err = nil
	}
	return err
}

func (c *Console) layout(g *gocui.Gui) error {
	err := c.drawTorrents(g)
	if err != nil {
		return err
	}
	err = c.drawStats(g)
	if err != nil {
		return err
	}
	_, err = g.SetCurrentView("torrents")
	return err
}

func (c *Console) drawTorrents(g *gocui.Gui) error {
	c.m.Lock()
	defer c.m.Unlock()

	maxX, maxY := g.Size()
	halfY := maxY / 2
	if v, err := g.SetView("torrents", -1, -1, maxX, halfY); err != nil {
		if err != gocui.ErrUnknownView {
			return err
		}
		v.Highlight = true
		v.SelBgColor = gocui.ColorGreen
		v.SelFgColor = gocui.ColorBlack
		fmt.Fprintln(v, "loading torrents...")
	} else {
		v.Clear()
		if c.errTorrents != nil {
			fmt.Fprintln(v, "error:", c.errTorrents)
			c.selectedID = 0
		} else {
			for _, t := range c.torrents {
				fmt.Fprintf(v, "%5d %s %s\n", t.ID, t.InfoHash, t.Name)
			}
		}
	}
	return nil
}

func (c *Console) drawStats(g *gocui.Gui) error {
	c.m.Lock()
	defer c.m.Unlock()

	maxX, maxY := g.Size()
	halfY := maxY / 2
	if v, err := g.SetView("stats", -1, halfY, maxX, maxY); err != nil {
		if err != gocui.ErrUnknownView {
			return err
		}
		v.Wrap = true
		fmt.Fprintln(v, "loading stats...")
	} else {
		v.Clear()
		if c.errStats != nil {
			fmt.Fprintln(v, "error:", c.errStats)
		} else {
			f := prettyjson.NewFormatter()
			f.Indent = 0
			f.Newline = ""
			b, err := f.Marshal(c.stats)
			if err != nil {
				fmt.Fprintln(v, "error:", c.errStats)
			} else {
				fmt.Fprintln(v, string(b))
			}
		}
	}
	return nil
}

func (c *Console) updateLoop(g *gocui.Gui) {
	ticker := time.NewTicker(time.Second)
	c.doUpdate(g)
	for {
		select {
		case <-ticker.C:
			c.doUpdate(g)
		case <-c.updateStatsC:
			c.doUpdateStats(g)
		}
	}
}

func (c *Console) doUpdate(g *gocui.Gui) {
	c.doUpdateTorrents(g)
	c.doUpdateStats(g)
}

func (c *Console) doUpdateTorrents(g *gocui.Gui) {
	resp, err := c.client.ListTorrents()

	sort.Slice(resp.Torrents, func(i, j int) bool { return resp.Torrents[i].ID < resp.Torrents[j].ID })

	c.m.Lock()
	c.torrents = resp.Torrents
	c.errTorrents = err
	if len(c.torrents) == 0 {
		c.setSelectedID(0)
	} else if c.selectedID == 0 {
		c.setSelectedID(c.torrents[0].ID)
	}
	c.m.Unlock()

	g.Update(c.drawTorrents)
}

func (c *Console) doUpdateStats(g *gocui.Gui) {
	c.m.Lock()
	selectedID := c.selectedID
	c.m.Unlock()

	if selectedID != 0 {
		resp, err := c.client.GetTorrentStats(selectedID)
		c.m.Lock()
		c.stats = resp.Stats
		c.errStats = err
		c.m.Unlock()
	} else {
		c.m.Lock()
		c.errStats = errors.New("no torrent selected")
		c.m.Unlock()
	}

	g.Update(c.drawStats)
}

func quit(g *gocui.Gui, v *gocui.View) error {
	return gocui.ErrQuit
}

func (c *Console) cursorDown(g *gocui.Gui, v *gocui.View) error {
	c.m.Lock()
	defer c.m.Unlock()

	cx, cy := v.Cursor()
	ox, oy := v.Origin()
	if cy+oy >= len(c.torrents)-1 {
		return nil
	}
	if err := v.SetCursor(cx, cy+1); err != nil {
		if err := v.SetOrigin(ox, oy+1); err != nil {
			return err
		}
	}
	c.setSelectedID(c.torrents[cy+oy+1].ID)
	return nil
}

func (c *Console) cursorUp(g *gocui.Gui, v *gocui.View) error {
	c.m.Lock()
	defer c.m.Unlock()

	cx, cy := v.Cursor()
	ox, oy := v.Origin()
	if cy+oy <= 0 {
		return nil
	}
	if err := v.SetCursor(cx, cy-1); err != nil && oy > 0 {
		if err := v.SetOrigin(ox, oy-1); err != nil {
			return err
		}
	}
	c.setSelectedID(c.torrents[cy+oy-1].ID)
	return nil
}

func (c *Console) removeTorrent(g *gocui.Gui, v *gocui.View) error {
	c.m.Lock()
	id := c.selectedID
	c.m.Unlock()

	_, err := c.client.RemoveTorrent(id)
	return err
}

func (c *Console) setSelectedID(id uint64) {
	changed := id != c.selectedID
	c.selectedID = id
	if changed {
		select {
		case c.updateStatsC <- struct{}{}:
		default:
		}
	}
}
<commit_msg>add start/stop commands<commit_after>package console

import (
	"errors"
	"fmt"
	"sort"
	"sync"
	"time"

	"github.com/cenkalti/rain/rainrpc"
	"github.com/cenkalti/rain/torrent"
	"github.com/hokaccha/go-prettyjson"
	"github.com/jroimartin/gocui"
)

type Console struct {
	client       *rainrpc.Client
	torrents     []rainrpc.Torrent
	errTorrents  error
	selectedID   uint64
	stats        torrent.Stats
	errStats     error
	m            sync.Mutex
	updateStatsC chan struct{}
}

func New(clt *rainrpc.Client) *Console {
	return &Console{
		client:       clt,
		updateStatsC: make(chan struct{}),
	}
}

func (c *Console) Run() error {
	g, err := gocui.NewGui(gocui.OutputNormal)
	if err != nil {
		return err
	}
	defer g.Close()

	g.SetManagerFunc(c.layout)

	g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit)
	g.SetKeybinding("", 'q', gocui.ModNone, quit)
	g.SetKeybinding("torrents", 'j', gocui.ModNone, c.cursorDown)
	g.SetKeybinding("torrents", 'k', gocui.ModNone, c.cursorUp)
	g.SetKeybinding("torrents", 'R', gocui.ModNone, c.removeTorrent)
	g.SetKeybinding("torrents", 's', gocui.ModNone, c.startTorrent)
	g.SetKeybinding("torrents", 'S', gocui.ModNone, c.stopTorrent)

	go c.updateLoop(g)

	err = g.MainLoop()
	if err == gocui.ErrQuit {
		err = nil
	}
	return err
}

func (c *Console) layout(g *gocui.Gui) error {
	err := c.drawTorrents(g)
	if err != nil {
		return err
	}
	err = c.drawStats(g)
	if err != nil {
		return err
	}
	_, err = g.SetCurrentView("torrents")
	return err
}

func (c *Console) drawTorrents(g *gocui.Gui) error {
	c.m.Lock()
	defer c.m.Unlock()

	maxX, maxY := g.Size()
	halfY := maxY / 2
	if v, err := g.SetView("torrents", -1, -1, maxX, halfY); err != nil {
		if err != gocui.ErrUnknownView {
			return err
		}
		v.Highlight = true
		v.SelBgColor = gocui.ColorGreen
		v.SelFgColor = gocui.ColorBlack
		fmt.Fprintln(v, "loading torrents...")
	} else {
		v.Clear()
		if c.errTorrents != nil {
			fmt.Fprintln(v, "error:", c.errTorrents)
			c.selectedID = 0
		} else {
			for _, t := range c.torrents {
				fmt.Fprintf(v, "%5d %s %s\n", t.ID, t.InfoHash, t.Name)
			}
		}
	}
	return nil
}

func (c *Console) drawStats(g *gocui.Gui) error {
	c.m.Lock()
	defer c.m.Unlock()

	maxX, maxY := g.Size()
	halfY := maxY / 2
	if v, err := g.SetView("stats", -1, halfY, maxX, maxY); err != nil {
		if err != gocui.ErrUnknownView {
			return err
		}
		v.Wrap = true
		fmt.Fprintln(v, "loading stats...")
	} else {
		v.Clear()
		if c.errStats != nil {
			fmt.Fprintln(v, "error:", c.errStats)
		} else {
			f := prettyjson.NewFormatter()
			f.Indent = 0
			f.Newline = ""
			b, err := f.Marshal(c.stats)
			if err != nil {
				fmt.Fprintln(v, "error:", c.errStats)
			} else {
				fmt.Fprintln(v, string(b))
			}
		}
	}
	return nil
}

func (c *Console) updateLoop(g *gocui.Gui) {
	ticker := time.NewTicker(time.Second)
	c.doUpdate(g)
	for {
		select {
		case <-ticker.C:
			c.doUpdate(g)
		case <-c.updateStatsC:
			c.doUpdateStats(g)
		}
	}
}

func (c *Console) doUpdate(g *gocui.Gui) {
	c.doUpdateTorrents(g)
	c.doUpdateStats(g)
}

func (c *Console) doUpdateTorrents(g *gocui.Gui) {
	resp, err := c.client.ListTorrents()

	sort.Slice(resp.Torrents, func(i, j int) bool { return resp.Torrents[i].ID < resp.Torrents[j].ID })

	c.m.Lock()
	c.torrents = resp.Torrents
	c.errTorrents = err
	if len(c.torrents) == 0 {
		c.setSelectedID(0)
	} else if c.selectedID == 0 {
		c.setSelectedID(c.torrents[0].ID)
	}
	c.m.Unlock()

	g.Update(c.drawTorrents)
}

func (c *Console) doUpdateStats(g *gocui.Gui) {
	c.m.Lock()
	selectedID := c.selectedID
	c.m.Unlock()

	if selectedID != 0 {
		resp, err := c.client.GetTorrentStats(selectedID)
		c.m.Lock()
		c.stats = resp.Stats
		c.errStats = err
		c.m.Unlock()
	} else {
		c.m.Lock()
		c.errStats = errors.New("no torrent selected")
		c.m.Unlock()
	}

	g.Update(c.drawStats)
}

func quit(g *gocui.Gui, v *gocui.View) error {
	return gocui.ErrQuit
}

func (c *Console) cursorDown(g *gocui.Gui, v *gocui.View) error {
	c.m.Lock()
	defer c.m.Unlock()

	cx, cy := v.Cursor()
	ox, oy := v.Origin()
	if cy+oy >= len(c.torrents)-1 {
		return nil
	}
	if err := v.SetCursor(cx, cy+1); err != nil {
		if err := v.SetOrigin(ox, oy+1); err != nil {
			return err
		}
	}
	c.setSelectedID(c.torrents[cy+oy+1].ID)
	return nil
}

func (c *Console) cursorUp(g *gocui.Gui, v *gocui.View) error {
	c.m.Lock()
	defer c.m.Unlock()

	cx, cy := v.Cursor()
	ox, oy := v.Origin()
	if cy+oy <= 0 {
		return nil
	}
	if err := v.SetCursor(cx, cy-1); err != nil && oy > 0 {
		if err := v.SetOrigin(ox, oy-1); err != nil {
			return err
		}
	}
	c.setSelectedID(c.torrents[cy+oy-1].ID)
	return nil
}

func (c *Console) removeTorrent(g *gocui.Gui, v *gocui.View) error {
	c.m.Lock()
	id := c.selectedID
	c.m.Unlock()

	_, err := c.client.RemoveTorrent(id)
	return err
}

func (c *Console) setSelectedID(id uint64) {
	changed := id != c.selectedID
	c.selectedID = id
	if changed {
		select {
		case c.updateStatsC <- struct{}{}:
		default:
		}
	}
}

func (c *Console) startTorrent(g *gocui.Gui, v *gocui.View) error {
	c.m.Lock()
	id := c.selectedID
	c.m.Unlock()

	_, err := c.client.StartTorrent(id)
	return err
}

func (c *Console) stopTorrent(g *gocui.Gui, v *gocui.View) error {
	c.m.Lock()
	id := c.selectedID
	c.m.Unlock()

	_, err := c.client.StopTorrent(id)
	return err
}
<|endoftext|>


<commit_before>// Copyright 2022 The Ebiten Authors
//
// 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 gamepad

import (
	"sync"

	"github.com/hajimehoshi/ebiten/v2/internal/driver"
	"github.com/hajimehoshi/ebiten/v2/internal/gamepaddb"
)

const (
	hatCentered  = 0
	hatUp        = 1
	hatRight     = 2
	hatDown      = 4
	hatLeft      = 8
	hatRightUp   = hatRight | hatUp
	hatRightDown = hatRight | hatDown
	hatLeftUp    = hatLeft | hatUp
	hatLeftDown  = hatLeft | hatDown
)

type gamepads struct {
	gamepads []*Gamepad
	m        sync.Mutex

	nativeGamepads
}

var theGamepads gamepads

func init() {
	theGamepads.nativeGamepads.init()
}

func AppendGamepadIDs(ids []driver.GamepadID) []driver.GamepadID {
	return theGamepads.appendGamepadIDs(ids)
}

func Update() {
	theGamepads.update()
}

func Get(id driver.GamepadID) *Gamepad {
	return theGamepads.get(id)
}

func (g *gamepads) appendGamepadIDs(ids []driver.GamepadID) []driver.GamepadID {
	g.m.Lock()
	defer g.m.Unlock()

	for i, gp := range g.gamepads {
		if gp != nil && gp.present() {
			ids = append(ids, driver.GamepadID(i))
		}
	}
	return ids
}

func (g *gamepads) update() {
	g.m.Lock()
	defer g.m.Unlock()

	for _, gp := range g.gamepads {
		if gp != nil {
			gp.update()
		}
	}
}

func (g *gamepads) get(id driver.GamepadID) *Gamepad {
	g.m.Lock()
	defer g.m.Unlock()

	if id < 0 || int(id) >= len(g.gamepads) {
		return nil
	}
	return g.gamepads[id]
}

func (g *gamepads) find(cond func(*Gamepad) bool) *Gamepad {
	g.m.Lock()
	defer g.m.Unlock()

	for _, gp := range g.gamepads {
		if gp == nil {
			continue
		}
		if cond(gp) {
			return gp
		}
	}
	return nil
}

func (g *gamepads) add(name, sdlID string) *Gamepad {
	g.m.Lock()
	defer g.m.Unlock()

	for i, gp := range g.gamepads {
		if gp == nil {
			gp := &Gamepad{
				name:  name,
				sdlID: sdlID,
			}
			g.gamepads[i] = gp
			return gp
		}
	}

	gp := &Gamepad{
		name:  name,
		sdlID: sdlID,
	}
	g.gamepads = append(g.gamepads, gp)
	return gp
}

func (g *gamepads) remove(cond func(*Gamepad) bool) {
	g.m.Lock()
	defer g.m.Unlock()

	for i, gp := range g.gamepads {
		if gp == nil {
			continue
		}
		if cond(gp) {
			g.gamepads[i] = nil
		}
	}
}

type Gamepad struct {
	name  string
	sdlID string
	m     sync.Mutex

	nativeGamepad
}

func (g *Gamepad) update() {
	g.m.Lock()
	defer g.m.Unlock()

	g.nativeGamepad.update()
}

func (g *Gamepad) Name() string {
	// This is immutable and doesn't have to be protected by a mutex.
	return g.name
}

func (g *Gamepad) SDLID() string {
	// This is immutable and doesn't have to be protected by a mutex.
	return g.sdlID
}

func (g *Gamepad) AxisNum() int {
	g.m.Lock()
	defer g.m.Unlock()

	return g.nativeGamepad.axisNum()
}

func (g *Gamepad) ButtonNum() int {
	g.m.Lock()
	defer g.m.Unlock()

	return g.nativeGamepad.buttonNum()
}

func (g *Gamepad) HatNum() int {
	g.m.Lock()
	defer g.m.Unlock()

	return g.nativeGamepad.hatNum()
}

func (g *Gamepad) Axis(axis int) float64 {
	g.m.Lock()
	defer g.m.Unlock()

	return g.nativeGamepad.axisValue(axis)
}

func (g *Gamepad) Button(button int) bool {
	g.m.Lock()
	defer g.m.Unlock()

	return g.nativeGamepad.isButtonPressed(button)
}

func (g *Gamepad) Hat(hat int) int {
	g.m.Lock()
	defer g.m.Unlock()

	return g.nativeGamepad.hatState(hat)
}

func (g *Gamepad) IsStandardLayoutAvailable() bool {
	g.m.Lock()
	defer g.m.Unlock()

	return gamepaddb.HasStandardLayoutMapping(g.sdlID)
}

func (g *Gamepad) StandardAxisValue(axis driver.StandardGamepadAxis) float64 {
	return gamepaddb.AxisValue(g.sdlID, axis, g)
}

func (g *Gamepad) StandardButtonValue(button driver.StandardGamepadButton) float64 {
	return gamepaddb.ButtonValue(g.sdlID, button, g)
}

func (g *Gamepad) IsStandardButtonPressed(button driver.StandardGamepadButton) bool {
	return gamepaddb.IsButtonPressed(g.sdlID, button, g)
}
<commit_msg>internal/gamepad: bug fix: go-vet failed<commit_after>// Copyright 2022 The Ebiten Authors
//
// 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.

//go:build darwin && !ios
// +build darwin,!ios

package gamepad

import (
	"sync"

	"github.com/hajimehoshi/ebiten/v2/internal/driver"
	"github.com/hajimehoshi/ebiten/v2/internal/gamepaddb"
)

const (
	hatCentered  = 0
	hatUp        = 1
	hatRight     = 2
	hatDown      = 4
	hatLeft      = 8
	hatRightUp   = hatRight | hatUp
	hatRightDown = hatRight | hatDown
	hatLeftUp    = hatLeft | hatUp
	hatLeftDown  = hatLeft | hatDown
)

type gamepads struct {
	gamepads []*Gamepad
	m        sync.Mutex

	nativeGamepads
}

var theGamepads gamepads

func init() {
	theGamepads.nativeGamepads.init()
}

func AppendGamepadIDs(ids []driver.GamepadID) []driver.GamepadID {
	return theGamepads.appendGamepadIDs(ids)
}

func Update() {
	theGamepads.update()
}

func Get(id driver.GamepadID) *Gamepad {
	return theGamepads.get(id)
}

func (g *gamepads) appendGamepadIDs(ids []driver.GamepadID) []driver.GamepadID {
	g.m.Lock()
	defer g.m.Unlock()

	for i, gp := range g.gamepads {
		if gp != nil && gp.present() {
			ids = append(ids, driver.GamepadID(i))
		}
	}
	return ids
}

func (g *gamepads) update() {
	g.m.Lock()
	defer g.m.Unlock()

	for _, gp := range g.gamepads {
		if gp != nil {
			gp.update()
		}
	}
}

func (g *gamepads) get(id driver.GamepadID) *Gamepad {
	g.m.Lock()
	defer g.m.Unlock()

	if id < 0 || int(id) >= len(g.gamepads) {
		return nil
	}
	return g.gamepads[id]
}

func (g *gamepads) find(cond func(*Gamepad) bool) *Gamepad {
	g.m.Lock()
	defer g.m.Unlock()

	for _, gp := range g.gamepads {
		if gp == nil {
			continue
		}
		if cond(gp) {
			return gp
		}
	}
	return nil
}

func (g *gamepads) add(name, sdlID string) *Gamepad {
	g.m.Lock()
	defer g.m.Unlock()

	for i, gp := range g.gamepads {
		if gp == nil {
			gp := &Gamepad{
				name:  name,
				sdlID: sdlID,
			}
			g.gamepads[i] = gp
			return gp
		}
	}

	gp := &Gamepad{
		name:  name,
		sdlID: sdlID,
	}
	g.gamepads = append(g.gamepads, gp)
	return gp
}

func (g *gamepads) remove(cond func(*Gamepad) bool) {
	g.m.Lock()
	defer g.m.Unlock()

	for i, gp := range g.gamepads {
		if gp == nil {
			continue
		}
		if cond(gp) {
			g.gamepads[i] = nil
		}
	}
}

type Gamepad struct {
	name  string
	sdlID string
	m     sync.Mutex

	nativeGamepad
}

func (g *Gamepad) update() {
	g.m.Lock()
	defer g.m.Unlock()

	g.nativeGamepad.update()
}

func (g *Gamepad) Name() string {
	// This is immutable and doesn't have to be protected by a mutex.
	return g.name
}

func (g *Gamepad) SDLID() string {
	// This is immutable and doesn't have to be protected by a mutex.
	return g.sdlID
}

func (g *Gamepad) AxisNum() int {
	g.m.Lock()
	defer g.m.Unlock()

	return g.nativeGamepad.axisNum()
}

func (g *Gamepad) ButtonNum() int {
	g.m.Lock()
	defer g.m.Unlock()

	return g.nativeGamepad.buttonNum()
}

func (g *Gamepad) HatNum() int {
	g.m.Lock()
	defer g.m.Unlock()

	return g.nativeGamepad.hatNum()
}

func (g *Gamepad) Axis(axis int) float64 {
	g.m.Lock()
	defer g.m.Unlock()

	return g.nativeGamepad.axisValue(axis)
}

func (g *Gamepad) Button(button int) bool {
	g.m.Lock()
	defer g.m.Unlock()

	return g.nativeGamepad.isButtonPressed(button)
}

func (g *Gamepad) Hat(hat int) int {
	g.m.Lock()
	defer g.m.Unlock()

	return g.nativeGamepad.hatState(hat)
}

func (g *Gamepad) IsStandardLayoutAvailable() bool {
	g.m.Lock()
	defer g.m.Unlock()

	return gamepaddb.HasStandardLayoutMapping(g.sdlID)
}

func (g *Gamepad) StandardAxisValue(axis driver.StandardGamepadAxis) float64 {
	return gamepaddb.AxisValue(g.sdlID, axis, g)
}

func (g *Gamepad) StandardButtonValue(button driver.StandardGamepadButton) float64 {
	return gamepaddb.ButtonValue(g.sdlID, button, g)
}

func (g *Gamepad) IsStandardButtonPressed(button driver.StandardGamepadButton) bool {
	return gamepaddb.IsButtonPressed(g.sdlID, button, g)
}
<|endoftext|>


<commit_before>// Copyright 2022 The Ebiten Authors
//
// 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 graphics

import (
	"bytes"
	"fmt"
	"go/parser"
	"go/token"

	"github.com/hajimehoshi/ebiten/v2/internal/shader"
	"github.com/hajimehoshi/ebiten/v2/internal/shaderir"
)

var shaderSuffix string

func init() {
	shaderSuffix = `
var __imageDstTextureSize vec2

// imageSrcTextureSize returns the destination image's texture size in pixels.
func imageDstTextureSize() vec2 {
	return __imageDstTextureSize
}
`

	shaderSuffix += fmt.Sprintf(`
var __textureSizes [%[1]d]vec2

// imageSrcTextureSize returns the source image's texture size in pixels.
// As an image is a part of internal texture, the texture is usually bigger than the image.
// The texture's size is useful when you want to calculate pixels from texels.
func imageSrcTextureSize() vec2 {
	return __textureSizes[0]
}

// The unit is the source texture's texel.
var __textureDestinationRegionOrigin vec2

// The unit is the source texture's texel.
var __textureDestinationRegionSize vec2

// imageDstRegionOnTexture returns the destination image's region (the origin and the size) on its texture.
// The unit is the source texture's texel.
//
// As an image is a part of internal texture, the image can be located at an arbitrary position on the texture.
func imageDstRegionOnTexture() (vec2, vec2) {
	return __textureDestinationRegionOrigin, __textureDestinationRegionSize
}

// The unit is the source texture's texel.
var __textureSourceOffsets [%[2]d]vec2

// The unit is the source texture's texel.
var __textureSourceRegionOrigin vec2

// The unit is the source texture's texel.
var __textureSourceRegionSize vec2

// imageSrcRegionOnTexture returns the source image's region (the origin and the size) on its texture.
// The unit is the source texture's texel.
//
// As an image is a part of internal texture, the image can be located at an arbitrary position on the texture.
func imageSrcRegionOnTexture() (vec2, vec2) {
	return __textureSourceRegionOrigin, __textureSourceRegionSize
}
`, ShaderImageNum, ShaderImageNum-1)

	for i := 0; i < ShaderImageNum; i++ {
		pos := "pos"
		if i >= 1 {
			// Convert the position in texture0's texels to the target texture texels.
			pos = fmt.Sprintf("(pos + __textureSourceOffsets[%d]) * __textureSizes[0] / __textureSizes[%d]", i-1, i)
		}
		// __t%d is a special variable for a texture variable.
		shaderSuffix += fmt.Sprintf(`
func imageSrc%[1]dUnsafeAt(pos vec2) vec4 {
	// pos is the position in texels of the source texture (= 0th image's texture).
	return texture2D(__t%[1]d, %[2]s)
}

func imageSrc%[1]dAt(pos vec2) vec4 {
	// pos is the position in texels of the source texture (= 0th image's texture).
	return texture2D(__t%[1]d, %[2]s) *
		step(__textureSourceRegionOrigin.x, pos.x) *
		(1 - step(__textureSourceRegionOrigin.x + __textureSourceRegionSize.x, pos.x)) *
		step(__textureSourceRegionOrigin.y, pos.y) *
		(1 - step(__textureSourceRegionOrigin.y + __textureSourceRegionSize.y, pos.y))
}
`, i, pos)
	}
}

func CompileShader(src []byte) (*shaderir.Program, error) {
	var buf bytes.Buffer
	buf.Write(src)
	buf.WriteString(shaderSuffix)
	buf.WriteString(`var __projectionMatrix mat4

func __vertex(position vec2, texCoord vec2, color vec4) (vec4, vec2, vec4) {
	return __projectionMatrix * vec4(position, 0, 1), texCoord, color
}
`)

	fs := token.NewFileSet()
	f, err := parser.ParseFile(fs, "", buf.Bytes(), parser.AllErrors)
	if err != nil {
		return nil, err
	}

	const (
		vert = "__vertex"
		frag = "Fragment"
	)
	ir, err := shader.Compile(fs, f, vert, frag, ShaderImageNum)
	if err != nil {
		return nil, err
	}

	if ir.VertexFunc.Block == nil {
		return nil, fmt.Errorf("graphicscommand: vertex shader entry point '%s' is missing", vert)
	}
	if ir.FragmentFunc.Block == nil {
		return nil, fmt.Errorf("graphicscommand: fragment shader entry point '%s' is missing", frag)
	}

	return ir, nil
}
<commit_msg>internal/graphics: refactoring<commit_after>// Copyright 2022 The Ebiten Authors
//
// 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 graphics

import (
	"bytes"
	"fmt"
	"go/parser"
	"go/token"

	"github.com/hajimehoshi/ebiten/v2/internal/shader"
	"github.com/hajimehoshi/ebiten/v2/internal/shaderir"
)

var shaderSuffix string

func init() {
	shaderSuffix = `
var __imageDstTextureSize vec2

// imageSrcTextureSize returns the destination image's texture size in pixels.
func imageDstTextureSize() vec2 {
	return __imageDstTextureSize
}
`

	shaderSuffix += fmt.Sprintf(`
var __textureSizes [%[1]d]vec2

// imageSrcTextureSize returns the source image's texture size in pixels.
// As an image is a part of internal texture, the texture is usually bigger than the image.
// The texture's size is useful when you want to calculate pixels from texels.
func imageSrcTextureSize() vec2 {
	return __textureSizes[0]
}

// The unit is the source texture's texel.
var __textureDestinationRegionOrigin vec2

// The unit is the source texture's texel.
var __textureDestinationRegionSize vec2

// imageDstRegionOnTexture returns the destination image's region (the origin and the size) on its texture.
// The unit is the source texture's texel.
//
// As an image is a part of internal texture, the image can be located at an arbitrary position on the texture.
func imageDstRegionOnTexture() (vec2, vec2) {
	return __textureDestinationRegionOrigin, __textureDestinationRegionSize
}

// The unit is the source texture's texel.
var __textureSourceOffsets [%[2]d]vec2

// The unit is the source texture's texel.
var __textureSourceRegionOrigin vec2

// The unit is the source texture's texel.
var __textureSourceRegionSize vec2

// imageSrcRegionOnTexture returns the source image's region (the origin and the size) on its texture.
// The unit is the source texture's texel.
//
// As an image is a part of internal texture, the image can be located at an arbitrary position on the texture.
func imageSrcRegionOnTexture() (vec2, vec2) {
	return __textureSourceRegionOrigin, __textureSourceRegionSize
}
`, ShaderImageNum, ShaderImageNum-1)

	for i := 0; i < ShaderImageNum; i++ {
		pos := "pos"
		if i >= 1 {
			// Convert the position in texture0's texels to the target texture texels.
			pos = fmt.Sprintf("(pos + __textureSourceOffsets[%d]) * __textureSizes[0] / __textureSizes[%d]", i-1, i)
		}
		// __t%d is a special variable for a texture variable.
		shaderSuffix += fmt.Sprintf(`
func imageSrc%[1]dUnsafeAt(pos vec2) vec4 {
	// pos is the position in texels of the source texture (= 0th image's texture).
	return texture2D(__t%[1]d, %[2]s)
}

func imageSrc%[1]dAt(pos vec2) vec4 {
	// pos is the position in texels of the source texture (= 0th image's texture).
	return texture2D(__t%[1]d, %[2]s) *
		step(__textureSourceRegionOrigin.x, pos.x) *
		(1 - step(__textureSourceRegionOrigin.x + __textureSourceRegionSize.x, pos.x)) *
		step(__textureSourceRegionOrigin.y, pos.y) *
		(1 - step(__textureSourceRegionOrigin.y + __textureSourceRegionSize.y, pos.y))
}
`, i, pos)
	}

	shaderSuffix += `
var __projectionMatrix mat4

func __vertex(position vec2, texCoord vec2, color vec4) (vec4, vec2, vec4) {
	return __projectionMatrix * vec4(position, 0, 1), texCoord, color
}
`
}

func CompileShader(src []byte) (*shaderir.Program, error) {
	var buf bytes.Buffer
	buf.Write(src)
	buf.WriteString(shaderSuffix)

	fs := token.NewFileSet()
	f, err := parser.ParseFile(fs, "", buf.Bytes(), parser.AllErrors)
	if err != nil {
		return nil, err
	}

	const (
		vert = "__vertex"
		frag = "Fragment"
	)
	ir, err := shader.Compile(fs, f, vert, frag, ShaderImageNum)
	if err != nil {
		return nil, err
	}

	if ir.VertexFunc.Block == nil {
		return nil, fmt.Errorf("graphicscommand: vertex shader entry point '%s' is missing", vert)
	}
	if ir.FragmentFunc.Block == nil {
		return nil, fmt.Errorf("graphicscommand: fragment shader entry point '%s' is missing", frag)
	}

	return ir, nil
}
<|endoftext|>


<commit_before>// Copyright 2014 Hajime Hoshi
//
// 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 graphics

type shaderId int

const (
	shaderVertexModelview shaderId = iota
	shaderFragmentTexture
)

func shader(id shaderId) string {
	return shaders[id]
}

var shaders = map[shaderId]string{
	shaderVertexModelview: `
uniform mat4 projection_matrix;
attribute vec2 vertex;
attribute vec4 tex_coord;
attribute vec4 geo_matrix_body;
attribute vec2 geo_matrix_translation;
varying vec2 varying_tex_coord;
varying vec2 varying_tex_coord_min;
varying vec2 varying_tex_coord_max;

void main(void) {
  varying_tex_coord = vec2(tex_coord[0], tex_coord[1]);
  varying_tex_coord_min =
    vec2(min(tex_coord[0], tex_coord[2]), min(tex_coord[1], tex_coord[3]));
  varying_tex_coord_max =
    vec2(max(tex_coord[0], tex_coord[2]), max(tex_coord[1], tex_coord[3]));
  mat4 geo_matrix = mat4(
    vec4(geo_matrix_body[0], geo_matrix_body[2], 0, 0),
    vec4(geo_matrix_body[1], geo_matrix_body[3], 0, 0),
    vec4(0, 0, 1, 0),
    vec4(geo_matrix_translation, 0, 1)
  );
  gl_Position = projection_matrix * geo_matrix * vec4(vertex, 0, 1);
}
`,
	shaderFragmentTexture: `
#if defined(GL_ES)
precision mediump float;
#else
#define lowp
#define mediump
#define highp
#endif

uniform sampler2D texture;
uniform mat4 color_matrix;
uniform vec4 color_matrix_translation;
uniform int filter_type;
uniform vec2 source_size;

varying vec2 varying_tex_coord;
varying vec2 varying_tex_coord_min;
varying vec2 varying_tex_coord_max;

vec2 roundTexel(vec2 p) {
  vec2 factor = 1.0 / (source_size * 256.0);
  if (factor.x > 0.0) {
    p.x -= mod(p.x + factor.x * 0.5, factor.x) - factor.x * 0.5;
  }
  if (factor.y > 0.0) {
    p.y -= mod(p.y + factor.y * 0.5, factor.y) - factor.y * 0.5;
  }
  return p;
}

vec4 getColorAt(vec2 pos) {
  if (pos.x < varying_tex_coord_min.x ||
      pos.y < varying_tex_coord_min.y ||
      varying_tex_coord_max.x <= pos.x ||
      varying_tex_coord_max.y <= pos.y) {
    return vec4(0, 0, 0, 0);
  }
  return texture2D(texture, pos);
}

void main(void) {
  vec4 color = vec4(0, 0, 0, 0);

  vec2 pos = roundTexel(varying_tex_coord);
  if (filter_type == 1) {
    // Nearest neighbor
    color = getColorAt(pos);
  } else if (filter_type == 2) {
    // Bi-linear
    vec2 texel_size = 1.0 / source_size;
    pos -= texel_size * 0.5;
    vec4 c0 = getColorAt(pos);
    vec4 c1 = getColorAt(pos + vec2(texel_size.x, 0));
    vec4 c2 = getColorAt(pos + vec2(0, texel_size.y));
    vec4 c3 = getColorAt(pos + texel_size);
    float rateX = fract(pos.x * source_size.x);
    float rateY = fract(pos.y * source_size.y);
    color = mix(mix(c0, c1, rateX), mix(c2, c3, rateX), rateY);
  } else {
    color = vec4(1, 0, 0, 1);
  }

  // Un-premultiply alpha
  if (0.0 < color.a) {
    color.rgb /= color.a;
  }
  // Apply the color matrix
  color = (color_matrix * color) + color_matrix_translation;
  color = clamp(color, 0.0, 1.0);
  // Premultiply alpha
  color.rgb *= color.a;

  gl_FragColor = color;
}
`,
}
<commit_msg>graphics: Temporary hack for mobile browers<commit_after>// Copyright 2014 Hajime Hoshi
//
// 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 graphics

import (
	"strings"

	"github.com/hajimehoshi/ebiten/internal/web"
)

type shaderId int

const (
	shaderVertexModelview shaderId = iota
	shaderFragmentTexture
)

func shader(id shaderId) string {
	s := shaders[id]
	defs := []string{}
	if web.IsMobileBrowser() {
		defs = append(defs, "#define IS_MOBILE_BROWSER")
	}
	s = strings.Replace(s, "{{Definitions}}", strings.Join(defs, "\n"), -1)
	return s
}

var shaders = map[shaderId]string{
	shaderVertexModelview: `
{{Definitions}}

uniform mat4 projection_matrix;
attribute vec2 vertex;
attribute vec4 tex_coord;
attribute vec4 geo_matrix_body;
attribute vec2 geo_matrix_translation;
varying vec2 varying_tex_coord;
varying vec2 varying_tex_coord_min;
varying vec2 varying_tex_coord_max;

void main(void) {
  varying_tex_coord = vec2(tex_coord[0], tex_coord[1]);
  varying_tex_coord_min =
    vec2(min(tex_coord[0], tex_coord[2]), min(tex_coord[1], tex_coord[3]));
  varying_tex_coord_max =
    vec2(max(tex_coord[0], tex_coord[2]), max(tex_coord[1], tex_coord[3]));
  mat4 geo_matrix = mat4(
    vec4(geo_matrix_body[0], geo_matrix_body[2], 0, 0),
    vec4(geo_matrix_body[1], geo_matrix_body[3], 0, 0),
    vec4(0, 0, 1, 0),
    vec4(geo_matrix_translation, 0, 1)
  );
  gl_Position = projection_matrix * geo_matrix * vec4(vertex, 0, 1);
}
`,
	shaderFragmentTexture: `
#if defined(GL_ES)
precision mediump float;
#else
#define lowp
#define mediump
#define highp
#endif

{{Definitions}}

uniform sampler2D texture;
uniform mat4 color_matrix;
uniform vec4 color_matrix_translation;
uniform vec2 source_size;
uniform int filter_type;

varying vec2 varying_tex_coord;
varying vec2 varying_tex_coord_min;
varying vec2 varying_tex_coord_max;

vec2 roundTexel(vec2 p) {
#if defined(IS_MOBILE_BROWSER)
  // This is a temporary hack: on mobile browsers like Android Chrome,
  // the code to round a float might cause unexpected behavior.
  // This is confirmed with rendering a relatively large (e.g. 4096x512) image
  // with specifying a source rectangle near its edges.
  // TODO: examples/moire doesn't work anyway.
  // TODO: Does this function work on an Android native app?
  return p;
#else
  vec2 factor = 1.0 / (source_size * 256.0);
  if (factor.x > 0.0) {
    p.x -= mod(p.x + factor.x * 0.5, factor.x) - factor.x * 0.5;
  }
  if (factor.y > 0.0) {
    p.y -= mod(p.y + factor.y * 0.5, factor.y) - factor.y * 0.5;
  }
  return p;
#endif
}

vec4 getColorAt(vec2 pos) {
  if (pos.x < varying_tex_coord_min.x ||
      pos.y < varying_tex_coord_min.y ||
      varying_tex_coord_max.x <= pos.x ||
      varying_tex_coord_max.y <= pos.y) {
    return vec4(0, 0, 0, 0);
  }
  return texture2D(texture, pos);
}

void main(void) {
  vec4 color = vec4(0, 0, 0, 0);

  vec2 pos = roundTexel(varying_tex_coord);
  if (filter_type == 1) {
    // Nearest neighbor
    color = getColorAt(pos);
  } else if (filter_type == 2) {
    // Bi-linear
    vec2 texel_size = 1.0 / source_size;
    pos -= texel_size * 0.5;
    vec4 c0 = getColorAt(pos);
    vec4 c1 = getColorAt(pos + vec2(texel_size.x, 0));
    vec4 c2 = getColorAt(pos + vec2(0, texel_size.y));
    vec4 c3 = getColorAt(pos + texel_size);
    float rateX = fract(pos.x * source_size.x);
    float rateY = fract(pos.y * source_size.y);
    color = mix(mix(c0, c1, rateX), mix(c2, c3, rateX), rateY);
  } else {
    color = vec4(1, 0, 0, 1);
  }

  // Un-premultiply alpha
  if (0.0 < color.a) {
    color.rgb /= color.a;
  }
  // Apply the color matrix
  color = (color_matrix * color) + color_matrix_translation;
  color = clamp(color, 0.0, 1.0);
  // Premultiply alpha
  color.rgb *= color.a;

  gl_FragColor = color;
}
`,
}
<|endoftext|>


<commit_before>package edgectl

import (
	"fmt"
	"net"
	"strconv"
	"strings"
	"time"

	"github.com/spf13/cobra"
	"github.com/spf13/pflag"

	"github.com/datawire/ambassador/pkg/supervisor"
)

func (d *Daemon) handleCommand(p *supervisor.Process, conn net.Conn, data *ClientMessage) error {
	out := NewEmitter(conn)
	rootCmd := d.GetRootCommand(p, out, data)
	rootCmd.SetOutput(conn) // FIXME replace with SetOut and SetErr
	rootCmd.PersistentPreRun = func(cmd *cobra.Command, _ []string) {
		if batch, _ := cmd.Flags().GetBool("batch"); batch {
			out.SetKV()
		}
	}
	rootCmd.SetArgs(data.Args[1:])
	err := rootCmd.Execute()
	if err != nil {
		out.SendExit(1)
	}
	return out.Err()
}

func (d *Daemon) GetRootCommand(p *supervisor.Process, out *Emitter, data *ClientMessage) *cobra.Command {
	rootCmd := &cobra.Command{
		Use:          "edgectl",
		Short:        "Edge Control",
		SilenceUsage: true, // https://github.com/spf13/cobra/issues/340
	}
	_ = rootCmd.PersistentFlags().Bool("batch", false, "Emit machine-readable output")
	_ = rootCmd.PersistentFlags().MarkHidden("batch")

	rootCmd.AddCommand(&cobra.Command{
		Use:   "version",
		Short: "Show program's version number and exit",
		Args:  cobra.ExactArgs(0),
		RunE: func(_ *cobra.Command, _ []string) error {
			out.Println("Client", data.ClientVersion)
			out.Println("Daemon", DisplayVersion())
			out.Send("daemon.version", Version)
			out.Send("daemon.apiVersion", apiVersion)
			return out.Err()
		},
	})
	rootCmd.AddCommand(&cobra.Command{
		Use:   "status",
		Short: "Show connectivity status",
		Args:  cobra.ExactArgs(0),
		RunE: func(_ *cobra.Command, _ []string) error {
			if err := d.Status(p, out); err != nil {
				return err
			}
			return out.Err()
		},
	})
	rootCmd.AddCommand(&cobra.Command{
		Use:   "pause",
		Short: "Turn off network overrides (to use a VPN)",
		Args:  cobra.ExactArgs(0),
		RunE: func(_ *cobra.Command, _ []string) error {
			if d.network == nil {
				out.Println("Network overrides are already paused")
				out.Send("paused", true)
				return out.Err()
			}
			if d.cluster != nil {
				out.Println("Edge Control is connected to a cluster.")
				out.Println("See \"edgectl status\" for details.")
				out.Println("Please disconnect before pausing.")
				out.Send("paused", false)
				out.SendExit(1)
				return out.Err()
			}

			if err := d.network.Close(); err != nil {
				p.Logf("pause: %v", err)
				out.Printf("Unexpected error while pausing: %v\n", err)
			}
			d.network = nil

			out.Println("Network overrides paused.")
			out.Println("Use \"edgectl resume\" to reestablish network overrides.")
			out.Send("paused", true)

			return out.Err()
		},
	})
	rootCmd.AddCommand(&cobra.Command{
		Use:     "resume",
		Short:   "Turn network overrides on (after using edgectl pause)",
		Aliases: []string{"unpause"},
		Args:    cobra.ExactArgs(0),
		RunE: func(_ *cobra.Command, _ []string) error {
			if d.network != nil {
				if d.network.IsOkay() {
					out.Println("Network overrides are established (not paused)")
				} else {
					out.Println("Network overrides are being reestablished...")
				}
				out.Send("paused", false)
				return out.Err()
			}

			if err := d.MakeNetOverride(p); err != nil {
				p.Logf("resume: %v", err)
				out.Printf("Unexpected error establishing network overrides: %v", err)
			}
			out.Send("paused", d.network == nil)

			return out.Err()
		},
	})
	connectCmd := &cobra.Command{
		Use:   "connect [flags] [-- additional kubectl arguments...]",
		Short: "Connect to a cluster",
		RunE: func(cmd *cobra.Command, args []string) error {
			context, _ := cmd.Flags().GetString("context")
			namespace, _ := cmd.Flags().GetString("namespace")
			managerNs, _ := cmd.Flags().GetString("manager-namespace")
			if err := d.Connect(p, out, data.RAI, context, namespace, managerNs, args); err != nil {
				return err
			}
			return out.Err()
		},
	}
	_ = connectCmd.Flags().StringP(
		"context", "c", "",
		"The Kubernetes context to use. Defaults to the current kubectl context.",
	)
	_ = connectCmd.Flags().StringP(
		"namespace", "n", "",
		"The Kubernetes namespace to use. Defaults to kubectl's default for the context.",
	)
	_ = connectCmd.Flags().StringP(
		"manager-namespace", "m", "ambassador",
		"The Kubernetes namespace in which the Traffic Manager is running.",
	)
	rootCmd.AddCommand(connectCmd)
	rootCmd.AddCommand(&cobra.Command{
		Use:   "disconnect",
		Short: "Disconnect from the connected cluster",
		Args:  cobra.ExactArgs(0),
		RunE: func(_ *cobra.Command, _ []string) error {
			if err := d.Disconnect(p, out); err != nil {
				return err
			}
			return out.Err()
		},
	})
	rootCmd.AddCommand(&cobra.Command{
		Use:   "quit",
		Short: "Tell Edge Control Daemon to quit (for upgrades)",
		Args:  cobra.ExactArgs(0),
		RunE: func(_ *cobra.Command, _ []string) error {
			out.Println("Edge Control Daemon quitting...")
			out.Send("quit", true)
			p.Supervisor().Shutdown()
			return out.Err()
		},
	})

	interceptCmd := &cobra.Command{
		Use: "intercept",
		Long: "Manage deployment intercepts. An intercept arranges for a subset of requests to be " +
			"diverted to the local machine.",
		Short: "Manage deployment intercepts",
	}
	interceptCmd.AddCommand(&cobra.Command{
		Use:     "available",
		Aliases: []string{"avail"},
		Short:   "List deployments available for intercept",
		Args:    cobra.ExactArgs(0),
		RunE: func(_ *cobra.Command, _ []string) error {
			msg := d.interceptMessage()
			if msg != "" {
				out.Println(msg)
				out.Send("intercept", msg)
				return out.Err()
			}
			out.Send("interceptable", len(d.trafficMgr.interceptables))
			switch {
			case len(d.trafficMgr.interceptables) == 0:
				out.Println("No interceptable deployments")
			default:
				out.Printf("Found %d interceptable deployment(s):\n", len(d.trafficMgr.interceptables))
				for idx, deployment := range d.trafficMgr.interceptables {
					fields := strings.SplitN(deployment, "/", 2)

					appName := fields[0]
					appNamespace := d.cluster.namespace

					if len(fields) > 1 {
						appNamespace = fields[0]
						appName = fields[1]
					}

					out.Printf("%4d. %s in namespace %s\n", idx+1, appName, appNamespace)
					out.Send(fmt.Sprintf("interceptable.deployment.%d", idx+1), deployment)
				}
			}
			return out.Err()
		},
	})

	interceptCmd.AddCommand(&cobra.Command{
		Use:   "list",
		Short: "List current intercepts",
		Args:  cobra.ExactArgs(0),
		RunE: func(_ *cobra.Command, _ []string) error {
			if err := d.ListIntercepts(p, out); err != nil {
				return err
			}
			return out.Err()
		},
	})
	interceptCmd.AddCommand(&cobra.Command{
		Use:   "remove",
		Short: "Deactivate and remove an existent intercept",
		Args:  cobra.MinimumNArgs(1),
		RunE: func(_ *cobra.Command, args []string) error {
			name := strings.TrimSpace(args[0])
			if err := d.RemoveIntercept(p, out, name); err != nil {
				return err
			}
			return out.Err()
		},
	})
	intercept := InterceptInfo{}
	interceptPreview := true
	var interceptAddCmdFlags *pflag.FlagSet
	interceptAddCmd := &cobra.Command{
		Use:   "add DEPLOYMENT -t [HOST:]PORT ([-p] | -m HEADER=REGEX ...)",
		Short: "Add a deployment intercept",
		Args:  cobra.ExactArgs(1),
		RunE: func(_ *cobra.Command, args []string) error {
			intercept.Deployment = args[0]
			if intercept.Name == "" {
				intercept.Name = fmt.Sprintf("cept-%d", time.Now().Unix())
			}

			// if intercept.Namespace == "" {
			// 	intercept.Namespace = "default"
			// }

			if intercept.Prefix == "" {
				intercept.Prefix = "/"
			}

			var host, portStr string
			hp := strings.SplitN(intercept.TargetHost, ":", 2)
			if len(hp) < 2 {
				portStr = hp[0]
			} else {
				host = strings.TrimSpace(hp[0])
				portStr = hp[1]
			}
			if len(host) == 0 {
				host = "127.0.0.1"
			}
			port, err := strconv.Atoi(portStr)
			if err != nil {
				out.Printf("Failed to parse %q as HOST:PORT: %v\n", intercept.TargetHost, err)
				out.Send("failed", "parse target")
				out.SendExit(1)
				return nil
			}
			intercept.TargetHost = host
			intercept.TargetPort = port

			// If the user specifies --preview on the command line, then use its
			// value (--preview is the same as --preview=true, or it could be
			// --preview=false). But if the user does not specify --preview on
			// the command line, compute its value from the presence or absence
			// of --match, since they are mutually exclusive.
			userSetPreviewFlag := interceptAddCmdFlags.Changed("preview")
			userSetMatchFlag := len(intercept.Patterns) > 0

			if userSetPreviewFlag && interceptPreview {
				// User specified --preview (or --preview=true) at the command line
				if userSetMatchFlag {
					out.Println("Error: Cannot use --match and --preview at the same time")
					out.Send("failed", "both match and preview")
					out.SendExit(1)
					return nil
				} else {
					// ok: --preview=true and no --match
				}
			} else if userSetPreviewFlag && !interceptPreview {
				// User specified --preview=false at the command line
				if userSetMatchFlag {
					// ok: --preview=false and at least one --match
				} else {
					out.Println("Error: Must specify --match when using --preview=false")
					out.Send("failed", "neither match nor preview")
					out.SendExit(1)
					return nil
				}
			} else {
				// User did not specify --preview at the command line
				if userSetMatchFlag {
					// ok: at least one --match
					interceptPreview = false
				} else {
					// ok: neither --match nor --preview, fall back to preview
					interceptPreview = true
				}
			}

			if interceptPreview {
				intercept.Patterns = make(map[string]string)
				intercept.Patterns["x-service-preview"] = data.InstallID
			}

			if err := d.AddIntercept(p, out, &intercept); err != nil {
				return err
			}

			if url := intercept.PreviewURL("$EDGE"); url != "" {
				out.Println("Share a preview of your changes with anyone by visiting\n  ", url)
			}

			return out.Err()
		},
	}
	interceptAddCmd.Flags().StringVarP(&intercept.Name, "name", "n", "", "a name for this intercept")
	interceptAddCmd.Flags().StringVar(&intercept.Prefix, "prefix", "/", "prefix to intercept (default /)")
	interceptAddCmd.Flags().BoolVarP(&interceptPreview, "preview", "p", true, "use a preview URL") // this default is unused
	interceptAddCmd.Flags().StringVarP(&intercept.TargetHost, "target", "t", "", "the [HOST:]PORT to forward to")
	_ = interceptAddCmd.MarkFlagRequired("target")
	interceptAddCmd.Flags().StringToStringVarP(&intercept.Patterns, "match", "m", nil, "match expression (HEADER=REGEX)")
	interceptAddCmd.Flags().StringVarP(&intercept.Namespace, "namespace", "", "", "Kubernetes namespace in which to create mapping for intercept")
	interceptAddCmdFlags = interceptAddCmd.Flags()

	interceptCmd.AddCommand(interceptAddCmd)
	interceptCG := []CmdGroup{
		{
			GroupName: "Available Commands",
			CmdNames:  []string{"available", "list", "add", "remove"},
		},
	}
	interceptCmd.SetUsageFunc(NewCmdUsage(interceptCmd, interceptCG))
	rootCmd.AddCommand(interceptCmd)

	return rootCmd
}
<commit_msg>edgectl: Improve CLI help/usage a bit for intercept commands<commit_after>package edgectl

import (
	"fmt"
	"net"
	"strconv"
	"strings"
	"time"

	"github.com/spf13/cobra"
	"github.com/spf13/pflag"

	"github.com/datawire/ambassador/pkg/supervisor"
)

func (d *Daemon) handleCommand(p *supervisor.Process, conn net.Conn, data *ClientMessage) error {
	out := NewEmitter(conn)
	rootCmd := d.GetRootCommand(p, out, data)
	rootCmd.SetOutput(conn) // FIXME replace with SetOut and SetErr
	rootCmd.PersistentPreRun = func(cmd *cobra.Command, _ []string) {
		if batch, _ := cmd.Flags().GetBool("batch"); batch {
			out.SetKV()
		}
	}
	rootCmd.SetArgs(data.Args[1:])
	err := rootCmd.Execute()
	if err != nil {
		out.SendExit(1)
	}
	return out.Err()
}

func (d *Daemon) GetRootCommand(p *supervisor.Process, out *Emitter, data *ClientMessage) *cobra.Command {
	rootCmd := &cobra.Command{
		Use:          "edgectl",
		Short:        "Edge Control",
		SilenceUsage: true, // https://github.com/spf13/cobra/issues/340
	}
	_ = rootCmd.PersistentFlags().Bool("batch", false, "Emit machine-readable output")
	_ = rootCmd.PersistentFlags().MarkHidden("batch")

	rootCmd.AddCommand(&cobra.Command{
		Use:   "version",
		Short: "Show program's version number and exit",
		Args:  cobra.ExactArgs(0),
		RunE: func(_ *cobra.Command, _ []string) error {
			out.Println("Client", data.ClientVersion)
			out.Println("Daemon", DisplayVersion())
			out.Send("daemon.version", Version)
			out.Send("daemon.apiVersion", apiVersion)
			return out.Err()
		},
	})
	rootCmd.AddCommand(&cobra.Command{
		Use:   "status",
		Short: "Show connectivity status",
		Args:  cobra.ExactArgs(0),
		RunE: func(_ *cobra.Command, _ []string) error {
			if err := d.Status(p, out); err != nil {
				return err
			}
			return out.Err()
		},
	})
	rootCmd.AddCommand(&cobra.Command{
		Use:   "pause",
		Short: "Turn off network overrides (to use a VPN)",
		Args:  cobra.ExactArgs(0),
		RunE: func(_ *cobra.Command, _ []string) error {
			if d.network == nil {
				out.Println("Network overrides are already paused")
				out.Send("paused", true)
				return out.Err()
			}
			if d.cluster != nil {
				out.Println("Edge Control is connected to a cluster.")
				out.Println("See \"edgectl status\" for details.")
				out.Println("Please disconnect before pausing.")
				out.Send("paused", false)
				out.SendExit(1)
				return out.Err()
			}

			if err := d.network.Close(); err != nil {
				p.Logf("pause: %v", err)
				out.Printf("Unexpected error while pausing: %v\n", err)
			}
			d.network = nil

			out.Println("Network overrides paused.")
			out.Println("Use \"edgectl resume\" to reestablish network overrides.")
			out.Send("paused", true)

			return out.Err()
		},
	})
	rootCmd.AddCommand(&cobra.Command{
		Use:     "resume",
		Short:   "Turn network overrides on (after using edgectl pause)",
		Aliases: []string{"unpause"},
		Args:    cobra.ExactArgs(0),
		RunE: func(_ *cobra.Command, _ []string) error {
			if d.network != nil {
				if d.network.IsOkay() {
					out.Println("Network overrides are established (not paused)")
				} else {
					out.Println("Network overrides are being reestablished...")
				}
				out.Send("paused", false)
				return out.Err()
			}

			if err := d.MakeNetOverride(p); err != nil {
				p.Logf("resume: %v", err)
				out.Printf("Unexpected error establishing network overrides: %v", err)
			}
			out.Send("paused", d.network == nil)

			return out.Err()
		},
	})
	connectCmd := &cobra.Command{
		Use:   "connect [flags] [-- additional kubectl arguments...]",
		Short: "Connect to a cluster",
		RunE: func(cmd *cobra.Command, args []string) error {
			context, _ := cmd.Flags().GetString("context")
			namespace, _ := cmd.Flags().GetString("namespace")
			managerNs, _ := cmd.Flags().GetString("manager-namespace")
			if err := d.Connect(p, out, data.RAI, context, namespace, managerNs, args); err != nil {
				return err
			}
			return out.Err()
		},
	}
	_ = connectCmd.Flags().StringP(
		"context", "c", "",
		"The Kubernetes context to use. Defaults to the current kubectl context.",
	)
	_ = connectCmd.Flags().StringP(
		"namespace", "n", "",
		"The Kubernetes namespace to use. Defaults to kubectl's default for the context.",
	)
	_ = connectCmd.Flags().StringP(
		"manager-namespace", "m", "ambassador",
		"The Kubernetes namespace in which the Traffic Manager is running.",
	)
	rootCmd.AddCommand(connectCmd)
	rootCmd.AddCommand(&cobra.Command{
		Use:   "disconnect",
		Short: "Disconnect from the connected cluster",
		Args:  cobra.ExactArgs(0),
		RunE: func(_ *cobra.Command, _ []string) error {
			if err := d.Disconnect(p, out); err != nil {
				return err
			}
			return out.Err()
		},
	})
	rootCmd.AddCommand(&cobra.Command{
		Use:   "quit",
		Short: "Tell Edge Control Daemon to quit (for upgrades)",
		Args:  cobra.ExactArgs(0),
		RunE: func(_ *cobra.Command, _ []string) error {
			out.Println("Edge Control Daemon quitting...")
			out.Send("quit", true)
			p.Supervisor().Shutdown()
			return out.Err()
		},
	})

	interceptCmd := &cobra.Command{
		Use: "intercept",
		Long: "Manage deployment intercepts. An intercept arranges for a subset of requests to be " +
			"diverted to the local machine.",
		Short: "Manage deployment intercepts",
	}
	interceptCmd.AddCommand(&cobra.Command{
		Use:     "available",
		Aliases: []string{"avail"},
		Short:   "List deployments available for intercept",
		Args:    cobra.ExactArgs(0),
		RunE: func(_ *cobra.Command, _ []string) error {
			msg := d.interceptMessage()
			if msg != "" {
				out.Println(msg)
				out.Send("intercept", msg)
				return out.Err()
			}
			out.Send("interceptable", len(d.trafficMgr.interceptables))
			switch {
			case len(d.trafficMgr.interceptables) == 0:
				out.Println("No interceptable deployments")
			default:
				out.Printf("Found %d interceptable deployment(s):\n", len(d.trafficMgr.interceptables))
				for idx, deployment := range d.trafficMgr.interceptables {
					fields := strings.SplitN(deployment, "/", 2)

					appName := fields[0]
					appNamespace := d.cluster.namespace

					if len(fields) > 1 {
						appNamespace = fields[0]
						appName = fields[1]
					}

					out.Printf("%4d. %s in namespace %s\n", idx+1, appName, appNamespace)
					out.Send(fmt.Sprintf("interceptable.deployment.%d", idx+1), deployment)
				}
			}
			return out.Err()
		},
	})

	interceptCmd.AddCommand(&cobra.Command{
		Use:   "list",
		Short: "List current intercepts",
		Args:  cobra.ExactArgs(0),
		RunE: func(_ *cobra.Command, _ []string) error {
			if err := d.ListIntercepts(p, out); err != nil {
				return err
			}
			return out.Err()
		},
	})
	interceptCmd.AddCommand(&cobra.Command{
		Use:     "remove [flags] DEPLOYMENT",
		Aliases: []string{"delete"},
		Short:   "Deactivate and remove an existent intercept",
		Args:    cobra.MinimumNArgs(1),
		RunE: func(_ *cobra.Command, args []string) error {
			name := strings.TrimSpace(args[0])
			if err := d.RemoveIntercept(p, out, name); err != nil {
				return err
			}
			return out.Err()
		},
	})
	intercept := InterceptInfo{}
	interceptPreview := true
	var interceptAddCmdFlags *pflag.FlagSet
	interceptAddCmd := &cobra.Command{
		Use:   "add [flags] DEPLOYMENT -t [HOST:]PORT ([-p] | -m HEADER=REGEX ...)",
		Short: "Add a deployment intercept",
		Args:  cobra.ExactArgs(1),
		RunE: func(_ *cobra.Command, args []string) error {
			intercept.Deployment = args[0]
			if intercept.Name == "" {
				intercept.Name = fmt.Sprintf("cept-%d", time.Now().Unix())
			}

			// if intercept.Namespace == "" {
			// 	intercept.Namespace = "default"
			// }

			if intercept.Prefix == "" {
				intercept.Prefix = "/"
			}

			var host, portStr string
			hp := strings.SplitN(intercept.TargetHost, ":", 2)
			if len(hp) < 2 {
				portStr = hp[0]
			} else {
				host = strings.TrimSpace(hp[0])
				portStr = hp[1]
			}
			if len(host) == 0 {
				host = "127.0.0.1"
			}
			port, err := strconv.Atoi(portStr)
			if err != nil {
				out.Printf("Failed to parse %q as HOST:PORT: %v\n", intercept.TargetHost, err)
				out.Send("failed", "parse target")
				out.SendExit(1)
				return nil
			}
			intercept.TargetHost = host
			intercept.TargetPort = port

			// If the user specifies --preview on the command line, then use its
			// value (--preview is the same as --preview=true, or it could be
			// --preview=false). But if the user does not specify --preview on
			// the command line, compute its value from the presence or absence
			// of --match, since they are mutually exclusive.
			userSetPreviewFlag := interceptAddCmdFlags.Changed("preview")
			userSetMatchFlag := len(intercept.Patterns) > 0

			if userSetPreviewFlag && interceptPreview {
				// User specified --preview (or --preview=true) at the command line
				if userSetMatchFlag {
					out.Println("Error: Cannot use --match and --preview at the same time")
					out.Send("failed", "both match and preview")
					out.SendExit(1)
					return nil
				} else {
					// ok: --preview=true and no --match
				}
			} else if userSetPreviewFlag && !interceptPreview {
				// User specified --preview=false at the command line
				if userSetMatchFlag {
					// ok: --preview=false and at least one --match
				} else {
					out.Println("Error: Must specify --match when using --preview=false")
					out.Send("failed", "neither match nor preview")
					out.SendExit(1)
					return nil
				}
			} else {
				// User did not specify --preview at the command line
				if userSetMatchFlag {
					// ok: at least one --match
					interceptPreview = false
				} else {
					// ok: neither --match nor --preview, fall back to preview
					interceptPreview = true
				}
			}

			if interceptPreview {
				intercept.Patterns = make(map[string]string)
				intercept.Patterns["x-service-preview"] = data.InstallID
			}

			if err := d.AddIntercept(p, out, &intercept); err != nil {
				return err
			}

			if url := intercept.PreviewURL("$EDGE"); url != "" {
				out.Println("Share a preview of your changes with anyone by visiting\n  ", url)
			}

			return out.Err()
		},
	}
	interceptAddCmd.Flags().StringVarP(&intercept.Name, "name", "n", "", "a name for this intercept")
	interceptAddCmd.Flags().StringVar(&intercept.Prefix, "prefix", "/", "prefix to intercept")
	interceptAddCmd.Flags().BoolVarP(&interceptPreview, "preview", "p", true, "use a preview URL") // this default is unused
	interceptAddCmd.Flags().StringVarP(&intercept.TargetHost, "target", "t", "", "the [HOST:]PORT to forward to")
	_ = interceptAddCmd.MarkFlagRequired("target")
	interceptAddCmd.Flags().StringToStringVarP(&intercept.Patterns, "match", "m", nil, "match expression (HEADER=REGEX)")
	interceptAddCmd.Flags().StringVarP(&intercept.Namespace, "namespace", "", "", "Kubernetes namespace in which to create mapping for intercept")
	interceptAddCmdFlags = interceptAddCmd.Flags()

	interceptCmd.AddCommand(interceptAddCmd)
	interceptCG := []CmdGroup{
		{
			GroupName: "Available Commands",
			CmdNames:  []string{"available", "list", "add", "remove"},
		},
	}
	interceptCmd.SetUsageFunc(NewCmdUsage(interceptCmd, interceptCG))
	rootCmd.AddCommand(interceptCmd)

	return rootCmd
}
<|endoftext|>


<commit_before>// +build !go1.3

package pool

import (
	"runtime"
)

type Pool struct {
	ch  chan interface{}
	New func() interface{}
}

// New returns a new Pool. The size argument is
// ignored on Go >= 1.3.
func New(size int) *Pool {
	if size == 0 {
		size = runtime.GOMAXPROCS(0)
	}
	return &Pool{ch: make(chan interface{}, size)}
}

func (p *Pool) Get() interface{} {
	select {
	case x := <-p.ch:
		return x
	default:
	}
	if p.New != nil {
		return p.New()
	}
	return nil
}

func (p *Pool) Put(x interface{}) {
	select {
	case p.ch <- x:
	default:
	}
}
<commit_msg>Increase default pool size to GOMAXPROCS() * 2<commit_after>// +build !go1.3

package pool

import (
	"runtime"
)

type Pool struct {
	ch  chan interface{}
	New func() interface{}
}

// New returns a new Pool. The size argument is
// ignored on Go >= 1.3.
func New(size int) *Pool {
	if size == 0 {
		size = runtime.GOMAXPROCS(0) * 2
	}
	return &Pool{ch: make(chan interface{}, size)}
}

func (p *Pool) Get() interface{} {
	select {
	case x := <-p.ch:
		return x
	default:
	}
	if p.New != nil {
		return p.New()
	}
	return nil
}

func (p *Pool) Put(x interface{}) {
	select {
	case p.ch <- x:
	default:
	}
}
<|endoftext|>


<commit_before>// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you 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 version

// Client returns the client version as a string.
//
const Client = "8.4.0-alpha.1"
<commit_msg>Update version to 8.4.0-SNAPSHOT<commit_after>// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you 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 version

// Client returns the client version as a string.
//
const Client = "8.4.0-SNAPSHOT"
<|endoftext|>


<commit_before>// Package watcher is used for watching files and directories
// for automatic recompilation and restart of app on change
// when in development mode.
package watcher

import (
	"errors"
	"os"
	"path/filepath"
	"sync"

	"github.com/anonx/sunplate/log"

	"gopkg.in/fsnotify.v1"
)

// Type is a watcher type that allows registering new
// pattern - actions pairs.
type Type struct {
	mu sync.Mutex
}

// NewType allocates and returns a new instance of watcher Type.
func NewType() *Type {
	return &Type{}
}

// Listen gets a pattern and a function. The function will be executed
// when files matching the pattern will be modified.
func (t *Type) Listen(pattern string, fn func()) {
	// Create a new watcher.
	w, err := fsnotify.NewWatcher()
	log.AssertNil(err)

	// Find directories matching the pattern.
	ds := glob(pattern)
	if err != nil {
		log.Error.Panicf("Pattern `%s` is malformed. Error: %v.", pattern, err)
	}

	// Add the files to the watcher.
	for i := range ds {
		log.Trace.Printf(`Adding "%s" to the list of watched directories...`, ds[i])
		err := w.Add(ds[i])
		if err != nil {
			log.Warn.Println(err)
		}
	}

	// Start watching process.
	go t.NotifyOnUpdate(w, fn)
}

// NotifyOnUpdate starts the function every time a file change
// event is received. Start it as a goroutine.
func (t *Type) NotifyOnUpdate(watcher *fsnotify.Watcher, fn func()) {
	for {
		select {
		case ev := <-watcher.Events:
			if restartRequired(ev) {
				t.mu.Lock()
				fn()
				t.mu.Unlock()
			}
		case err := <-watcher.Errors:
			log.Warn.Println(err)
		}
	}
}

// restartRequired checks whether event indicates a file
// has been modified. If so, it returns true.
func restartRequired(event fsnotify.Event) bool {
	if event.Op&fsnotify.Write == fsnotify.Write {
		return true
	}
	return false
}

// glob returns names of all directories matching pattern or nil.
// The only supported special character is an asterisk at the end.
// It means that the directory is expected to be scanned recursively.
// There is no way for fsnotify to watch individual files (see #17),
// do we support only directories.
// File system errors such as I/O reading are ignored.
func glob(pattern string) (ds []string) {
	// Check whether recursive scan is expected.
	l := len(pattern)
	if l == 0 || pattern[l-1] != '*' {
		ds = append(ds, pattern)
		return // Return as is.
	}

	// Otherwise, trim the asterisk at the end.
	pattern = pattern[:l-1]

	// Start searching directories recursively.
	filepath.Walk(pattern, func(path string, info os.FileInfo, err error) error {
		// Make sure there are no any errors.
		if err != nil {
			return err
		}

		// Make sure the path represents a directory.
		if info.IsDir() {
			ds = append(ds, path) // Add current path to the list.
			return nil
		}

		// Otherwise, return an error.
		return errors.New("not a directory")
	})
	return
}
<commit_msg>Implemented ListenFile of watcher<commit_after>// Package watcher is used for watching files and directories
// for automatic recompilation and restart of app on change
// when in development mode.
package watcher

import (
	"errors"
	"os"
	"path/filepath"
	"sync"

	"github.com/anonx/sunplate/log"

	"gopkg.in/fsnotify.v1"
)

// Type is a watcher type that allows registering new
// pattern - actions pairs.
type Type struct {
	mu    sync.Mutex
	files map[string]bool
}

// NewType allocates and returns a new instance of watcher Type.
func NewType() *Type {
	return &Type{
		files: map[string]bool{},
	}
}

// Listen gets a pattern and a function. The function will be executed
// when files matching the pattern will be modified.
func (t *Type) Listen(pattern string, fn func()) {
	// Create a new watcher.
	w, err := fsnotify.NewWatcher()
	log.AssertNil(err)

	// Find directories matching the pattern.
	ds := glob(pattern)

	// Add the files to the watcher.
	for i := range ds {
		log.Trace.Printf(`Adding "%s" to the list of watched directories...`, ds[i])
		err := w.Add(ds[i])
		if err != nil {
			log.Warn.Println(err)
		}
	}

	// Start watching process.
	go t.NotifyOnUpdate(pattern, w, fn)
}

// ListenFile is equivalent of Listen but for files.
// If file is added using ListenFile and the same file
// is withing a pattern of Listen, only the first one
// will trigger restarts.
// I.e. we have the following calls:
//	w.Listen("./", fn1)
//	w.ListenFile("./sunplate.yml", fn2)
// If "sunplate.yml" file is modified fn2 will be triggered.
// fn1 may be triggered by changes in any file inside
// "./" directory except "sunplate.yml".
func (t *Type) ListenFile(path string, fn func()) {
	// Create a new watcher.
	w, err := fsnotify.NewWatcher()
	log.AssertNil(err)

	// Watch a directory instead of file.
	// See issue #17 of fsnotify to find out more
	// why we do this.
	dir := filepath.Join(path, "../")
	w.Add(dir)

	// Start watching process.
	t.files[path] = true
	go t.NotifyOnUpdate(path, w, fn)
}

// NotifyOnUpdate starts the function every time a file change
// event is received. Start it as a goroutine.
func (t *Type) NotifyOnUpdate(pattern string, watcher *fsnotify.Watcher, fn func()) {
	for {
		select {
		case ev := <-watcher.Events:
			// If modified file was added using ListenFile,
			// allow restarts only of its own function.
			restartAllowed := true
			if t.files[ev.Name] && ev.Name != pattern {
				restartAllowed = false
			}

			// Check whether file has been modified and
			// this watcher is allowed to restart function
			// after changes to this file.
			if restartRequired(ev) && restartAllowed {
				t.mu.Lock()
				fn()
				t.mu.Unlock()
			}
		case err := <-watcher.Errors:
			log.Warn.Println(err)
		}
	}
}

// restartRequired checks whether event indicates a file
// has been modified. If so, it returns true.
func restartRequired(event fsnotify.Event) bool {
	if event.Op&fsnotify.Write == fsnotify.Write {
		return true
	}
	return false
}

// glob returns names of all directories matching pattern or nil.
// The only supported special character is an asterisk at the end.
// It means that the directory is expected to be scanned recursively.
// There is no way for fsnotify to watch individual files (see #17),
// so we support only directories.
// File system errors such as I/O reading are ignored.
func glob(pattern string) (ds []string) {
	// Make sure pattern is not empty.
	l := len(pattern)
	if l == 0 {
		return
	}

	// Check whether we should scan the directory recursively.
	recurs := pattern[l-1] == '*'
	if recurs {
		// Trim the asterisk at the end.
		pattern = pattern[:l-1]
	}

	// Make sure such path exists and it is a directory rather than a file.
	info, err := os.Stat(pattern)
	if err != nil {
		return
	}
	if !info.IsDir() {
		log.Warn.Printf(`"%s" is not a directory, skipping it.`, pattern)
		return
	}

	// If not recursive scan was expected, return the path as is.
	if !recurs {
		ds = append(ds, pattern)
		return // Return as is.
	}

	// Start searching directories recursively.
	filepath.Walk(pattern, func(path string, info os.FileInfo, err error) error {
		// Make sure there are no any errors.
		if err != nil {
			return err
		}

		// Make sure the path represents a directory.
		if info.IsDir() {
			ds = append(ds, path) // Add current path to the list.
			return nil
		}

		// Otherwise, return an error.
		return errors.New("not a directory")
	})
	return
}
<|endoftext|>