<commit_before>package build

import (
	"io/ioutil"
	"strings"

	"gopkg.in/yaml.v2"
)

// Artefact holds a parsed source for a build artefact
type Artefact struct {
	Order  int
	Step   Step
	Source string
	Dest   string // this is only the folder. Filename comes from the source
}

// Step Holds a single step in the build process
// Public structs. They are used to store the build for the builders
type Step struct {
	Order      int
	Name       string
	Dockerfile string
	Keep       bool
	Artefacts  []Artefact
	Manifest   Manifest
}

// Manifest Holds the whole build process
type Manifest struct {
	Workdir string
	Steps   []Step
}

// Private structs. They are used to load from yaml
type step struct {
	Name       string
	Dockerfile string
	Keep       bool
	Artefacts  []string
}

type build struct {
	Workdir string
	Steps   []step
}

// LoadBuildFromFile loads Build from a yaml file
func LoadBuildFromFile(file string) (*Manifest, error) {
	t := build{}

	data, err := ioutil.ReadFile(file)
	if err != nil {
		return nil, err
	}

	err = yaml.Unmarshal([]byte(data), &t)
	if err != nil {
		return nil, err
	}

	return t.convertToBuild()
}

func (b *build) convertToBuild() (*Manifest, error) {
	r := Manifest{}
	r.Workdir = b.Workdir
	r.Steps = []Step{}

	for idx, s := range b.Steps {
		convertedStep := Step{}

		convertedStep.Manifest = r
		convertedStep.Dockerfile = s.Dockerfile
		convertedStep.Name = s.Name
		convertedStep.Order = idx
		convertedStep.Keep = s.Keep
		convertedStep.Artefacts = []Artefact{}

		for kdx, a := range s.Artefacts {
			convertedArt := Artefact{}

			convertedArt.Order = kdx
			convertedArt.Step = convertedStep
			parts := strings.Split(a, ":")
			convertedArt.Source = parts[0]
			if len(parts) == 1 {
				// only one use the base
				convertedArt.Dest = "."
			} else {
				convertedArt.Dest = parts[1]
			}

			convertedStep.Artefacts = append(convertedStep.Artefacts, convertedArt)
		}

		// TODO: validate (unique name,...)

		r.Steps = append(r.Steps, convertedStep)
	}

	return &r, nil
}

// FindStepByName finds a step by name. Returns nil if not found
func (m *Manifest) FindStepByName(name string) (*Step, error) {
	for _, step := range m.Steps {
		if step.Name == name {
			return &step, nil
		}
	}

	return nil, nil
}
<commit_msg>check for step name uniqueness<commit_after>package build

import (
	"fmt"
	"io/ioutil"
	"strings"

	"gopkg.in/yaml.v2"
)

// Artefact holds a parsed source for a build artefact
type Artefact struct {
	Order  int
	Step   Step
	Source string
	Dest   string // this is only the folder. Filename comes from the source
}

// Step Holds a single step in the build process
// Public structs. They are used to store the build for the builders
type Step struct {
	Order      int
	Name       string
	Dockerfile string
	Keep       bool
	Artefacts  []Artefact
	Manifest   Manifest
}

// Manifest Holds the whole build process
type Manifest struct {
	Workdir string
	Steps   []Step
}

// Private structs. They are used to load from yaml
type step struct {
	Name       string
	Dockerfile string
	Keep       bool
	Artefacts  []string
}

type build struct {
	Workdir string
	Steps   []step
}

// LoadBuildFromFile loads Build from a yaml file
func LoadBuildFromFile(file string) (*Manifest, error) {
	t := build{}

	data, err := ioutil.ReadFile(file)
	if err != nil {
		return nil, err
	}

	err = yaml.Unmarshal([]byte(data), &t)
	if err != nil {
		return nil, err
	}

	return t.convertToBuild()
}

func (b *build) convertToBuild() (*Manifest, error) {
	r := Manifest{}
	r.Workdir = b.Workdir
	r.Steps = []Step{}

	for idx, s := range b.Steps {
		convertedStep := Step{}

		convertedStep.Manifest = r
		convertedStep.Dockerfile = s.Dockerfile
		convertedStep.Name = s.Name
		convertedStep.Order = idx
		convertedStep.Keep = s.Keep
		convertedStep.Artefacts = []Artefact{}

		for kdx, a := range s.Artefacts {
			convertedArt := Artefact{}

			convertedArt.Order = kdx
			convertedArt.Step = convertedStep
			parts := strings.Split(a, ":")
			convertedArt.Source = parts[0]
			if len(parts) == 1 {
				// only one use the base
				convertedArt.Dest = "."
			} else {
				convertedArt.Dest = parts[1]
			}

			convertedStep.Artefacts = append(convertedStep.Artefacts, convertedArt)
		}

		// is it unique?
		for _, s := range r.Steps {
			if s.Name == convertedStep.Name {
				return nil, fmt.Errorf("Step name '%s' is not unique", convertedStep.Name)
			}
		}

		r.Steps = append(r.Steps, convertedStep)
	}

	return &r, nil
}

// FindStepByName finds a step by name. Returns nil if not found
func (m *Manifest) FindStepByName(name string) (*Step, error) {
	for _, step := range m.Steps {
		if step.Name == name {
			return &step, nil
		}
	}

	return nil, 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 ebiten

import (
	"fmt"
	"math"
	"sync"

	"github.com/hajimehoshi/ebiten/internal/buffered"
	"github.com/hajimehoshi/ebiten/internal/clock"
	"github.com/hajimehoshi/ebiten/internal/driver"
	"github.com/hajimehoshi/ebiten/internal/graphicscommand"
	"github.com/hajimehoshi/ebiten/internal/hooks"
	"github.com/hajimehoshi/ebiten/internal/shareable"
)

func init() {
	shareable.SetGraphicsDriver(graphicsDriver())
	graphicscommand.SetGraphicsDriver(graphicsDriver())
}

type defaultGame struct {
	update func(screen *Image) error
	width  int
	height int
}

func (d *defaultGame) Update(screen *Image) error {
	return d.update(screen)
}

func (d *defaultGame) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) {
	// Ignore the outside size.
	return d.width, d.height
}

func (d *defaultGame) setScreenSize(width, height int) {
	d.width = width
	d.height = height
}

func newUIContext(game Game, width, height int, scaleForWindow float64) *uiContext {
	return &uiContext{
		game:           game,
		scaleForWindow: scaleForWindow,
	}
}

type uiContext struct {
	game           Game
	offscreen      *Image
	screen         *Image
	screenScale    float64
	scaleForWindow float64
	offsetX        float64
	offsetY        float64

	outsideSizeUpdated bool
	outsideWidth       float64
	outsideHeight      float64

	m sync.Mutex
}

var theUIContext *uiContext

func (c *uiContext) setScaleForWindow(scale float64) {
	g, ok := c.game.(*defaultGame)
	if !ok {
		panic("ebiten: setScaleForWindow must be called when Run is used")
	}
	w, h := g.width, g.height
	c.m.Lock()
	c.scaleForWindow = scale
	uiDriver().SetWindowSize(int(float64(w)*scale), int(float64(h)*scale))
	c.m.Unlock()
}

func (c *uiContext) getScaleForWindow() float64 {
	if _, ok := c.game.(*defaultGame); !ok {
		panic("ebiten: getScaleForWindow must be called when Run is used")
	}
	c.m.Lock()
	s := c.scaleForWindow
	c.m.Unlock()
	return s
}

func (c *uiContext) SetScreenSize(width, height int) {
	c.m.Lock()
	defer c.m.Unlock()

	g, ok := c.game.(*defaultGame)
	if !ok {
		panic("ebiten: SetScreenSize must be called when Run is used")
	}
	g.setScreenSize(width, height)
	s := c.scaleForWindow
	uiDriver().SetWindowSize(int(float64(width)*s), int(float64(height)*s))
}

func (c *uiContext) Layout(outsideWidth, outsideHeight float64) {
	c.outsideSizeUpdated = true
	c.outsideWidth = outsideWidth
	c.outsideHeight = outsideHeight
}

func (c *uiContext) updateOffscreen() {
	sw, sh := c.game.Layout(int(c.outsideWidth), int(c.outsideHeight))

	if c.offscreen != nil && !c.outsideSizeUpdated {
		if w, h := c.offscreen.Size(); w == sw && h == sh {
			return
		}
	}
	c.outsideSizeUpdated = false

	if c.screen != nil {
		_ = c.screen.Dispose()
		c.screen = nil
	}

	if c.offscreen != nil {
		if w, h := c.offscreen.Size(); w != sw || h != sh {
			_ = c.offscreen.Dispose()
			c.offscreen = nil
		}
	}
	if c.offscreen == nil {
		c.offscreen = newImage(sw, sh, FilterDefault, true)
	}
	c.SetScreenSize(sw, sh)

	// TODO: This is duplicated with mobile/ebitenmobileview/funcs.go. Refactor this.
	d := uiDriver().DeviceScaleFactor()
	c.screen = newScreenFramebufferImage(int(c.outsideWidth*d), int(c.outsideHeight*d))

	scaleX := c.outsideWidth / float64(sw) * d
	scaleY := c.outsideHeight / float64(sh) * d
	c.screenScale = math.Min(scaleX, scaleY)
	if uiDriver().CanHaveWindow() && !uiDriver().IsFullscreen() {
		c.setScaleForWindow(c.screenScale / d)
	}

	width := float64(sw) * c.screenScale
	height := float64(sh) * c.screenScale
	c.offsetX = (c.outsideWidth*d - width) / 2
	c.offsetY = (c.outsideHeight*d - height) / 2
}

func (c *uiContext) Update(afterFrameUpdate func()) error {
	updateCount := clock.Update(MaxTPS())

	// TODO: If updateCount is 0 and vsync is disabled, swapping buffers can be skipped.

	if err := buffered.BeginFrame(); err != nil {
		return err
	}

	for i := 0; i < updateCount; i++ {
		c.updateOffscreen()

		// Mipmap images should be disposed by Clear.
		c.offscreen.Clear()

		setDrawingSkipped(i < updateCount-1)

		if err := hooks.RunBeforeUpdateHooks(); err != nil {
			return err
		}
		if err := c.game.Update(c.offscreen); err != nil {
			return err
		}
		uiDriver().Input().ResetForFrame()
		afterFrameUpdate()
	}

	// This clear is needed for fullscreen mode or some mobile platforms (#622).
	c.screen.Clear()

	op := &DrawImageOptions{}

	switch vd := graphicsDriver().VDirection(); vd {
	case driver.VDownward:
		// c.screen is special: its Y axis is down to up,
		// and the origin point is lower left.
		op.GeoM.Scale(c.screenScale, -c.screenScale)
		_, h := c.offscreen.Size()
		op.GeoM.Translate(0, float64(h)*c.screenScale)
	case driver.VUpward:
		op.GeoM.Scale(c.screenScale, c.screenScale)
	default:
		panic(fmt.Sprintf("ebiten: invalid v-direction: %d", vd))
	}

	op.GeoM.Translate(c.offsetX, c.offsetY)
	op.CompositeMode = CompositeModeCopy

	// filterScreen works with >=1 scale, but does not well with <1 scale.
	// Use regular FilterLinear instead so far (#669).
	if c.screenScale >= 1 {
		op.Filter = filterScreen
	} else {
		op.Filter = FilterLinear
	}
	_ = c.screen.DrawImage(c.offscreen, op)

	if err := buffered.EndFrame(); err != nil {
		return err
	}
	return nil
}

func (c *uiContext) AdjustPosition(x, y float64) (float64, float64) {
	d := uiDriver().DeviceScaleFactor()
	return (x*d - c.offsetX) / c.screenScale, (y*d - c.offsetY) / c.screenScale
}
<commit_msg>ui: Protect defaultGame's memers with mutex<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 ebiten

import (
	"fmt"
	"math"
	"sync"

	"github.com/hajimehoshi/ebiten/internal/buffered"
	"github.com/hajimehoshi/ebiten/internal/clock"
	"github.com/hajimehoshi/ebiten/internal/driver"
	"github.com/hajimehoshi/ebiten/internal/graphicscommand"
	"github.com/hajimehoshi/ebiten/internal/hooks"
	"github.com/hajimehoshi/ebiten/internal/shareable"
)

func init() {
	shareable.SetGraphicsDriver(graphicsDriver())
	graphicscommand.SetGraphicsDriver(graphicsDriver())
}

type defaultGame struct {
	update  func(screen *Image) error
	width   int
	height  int
	context *uiContext
}

func (d *defaultGame) Update(screen *Image) error {
	return d.update(screen)
}

func (d *defaultGame) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) {
	// Ignore the outside size.
	d.context.m.Lock()
	w, h := d.width, d.height
	d.context.m.Unlock()
	return w, h
}

func newUIContext(game Game, width, height int, scaleForWindow float64) *uiContext {
	u := &uiContext{
		game:           game,
		scaleForWindow: scaleForWindow,
	}
	if g, ok := game.(*defaultGame); ok {
		g.context = u
	}
	return u
}

type uiContext struct {
	game           Game
	offscreen      *Image
	screen         *Image
	screenScale    float64
	scaleForWindow float64
	offsetX        float64
	offsetY        float64

	outsideSizeUpdated bool
	outsideWidth       float64
	outsideHeight      float64

	m sync.Mutex
}

var theUIContext *uiContext

func (c *uiContext) setScaleForWindow(scale float64) {
	g, ok := c.game.(*defaultGame)
	if !ok {
		panic("ebiten: setScaleForWindow must be called when Run is used")
	}

	c.m.Lock()
	defer c.m.Unlock()

	w, h := g.width, g.height
	c.scaleForWindow = scale
	uiDriver().SetWindowSize(int(float64(w)*scale), int(float64(h)*scale))
}

func (c *uiContext) getScaleForWindow() float64 {
	if _, ok := c.game.(*defaultGame); !ok {
		panic("ebiten: getScaleForWindow must be called when Run is used")
	}

	c.m.Lock()
	s := c.scaleForWindow
	c.m.Unlock()
	return s
}

func (c *uiContext) SetScreenSize(width, height int) {
	g, ok := c.game.(*defaultGame)
	if !ok {
		panic("ebiten: SetScreenSize must be called when Run is used")
	}

	c.m.Lock()
	defer c.m.Unlock()

	g.width = width
	g.height = height
	s := c.scaleForWindow
	uiDriver().SetWindowSize(int(float64(width)*s), int(float64(height)*s))
}

func (c *uiContext) Layout(outsideWidth, outsideHeight float64) {
	c.outsideSizeUpdated = true
	c.outsideWidth = outsideWidth
	c.outsideHeight = outsideHeight
}

func (c *uiContext) updateOffscreen() {
	sw, sh := c.game.Layout(int(c.outsideWidth), int(c.outsideHeight))

	if c.offscreen != nil && !c.outsideSizeUpdated {
		if w, h := c.offscreen.Size(); w == sw && h == sh {
			return
		}
	}
	c.outsideSizeUpdated = false

	if c.screen != nil {
		_ = c.screen.Dispose()
		c.screen = nil
	}

	if c.offscreen != nil {
		if w, h := c.offscreen.Size(); w != sw || h != sh {
			_ = c.offscreen.Dispose()
			c.offscreen = nil
		}
	}
	if c.offscreen == nil {
		c.offscreen = newImage(sw, sh, FilterDefault, true)
	}
	c.SetScreenSize(sw, sh)

	// TODO: This is duplicated with mobile/ebitenmobileview/funcs.go. Refactor this.
	d := uiDriver().DeviceScaleFactor()
	c.screen = newScreenFramebufferImage(int(c.outsideWidth*d), int(c.outsideHeight*d))

	scaleX := c.outsideWidth / float64(sw) * d
	scaleY := c.outsideHeight / float64(sh) * d
	c.screenScale = math.Min(scaleX, scaleY)
	if uiDriver().CanHaveWindow() && !uiDriver().IsFullscreen() {
		c.setScaleForWindow(c.screenScale / d)
	}

	width := float64(sw) * c.screenScale
	height := float64(sh) * c.screenScale
	c.offsetX = (c.outsideWidth*d - width) / 2
	c.offsetY = (c.outsideHeight*d - height) / 2
}

func (c *uiContext) Update(afterFrameUpdate func()) error {
	updateCount := clock.Update(MaxTPS())

	// TODO: If updateCount is 0 and vsync is disabled, swapping buffers can be skipped.

	if err := buffered.BeginFrame(); err != nil {
		return err
	}

	for i := 0; i < updateCount; i++ {
		c.updateOffscreen()

		// Mipmap images should be disposed by Clear.
		c.offscreen.Clear()

		setDrawingSkipped(i < updateCount-1)

		if err := hooks.RunBeforeUpdateHooks(); err != nil {
			return err
		}
		if err := c.game.Update(c.offscreen); err != nil {
			return err
		}
		uiDriver().Input().ResetForFrame()
		afterFrameUpdate()
	}

	// This clear is needed for fullscreen mode or some mobile platforms (#622).
	c.screen.Clear()

	op := &DrawImageOptions{}

	switch vd := graphicsDriver().VDirection(); vd {
	case driver.VDownward:
		// c.screen is special: its Y axis is down to up,
		// and the origin point is lower left.
		op.GeoM.Scale(c.screenScale, -c.screenScale)
		_, h := c.offscreen.Size()
		op.GeoM.Translate(0, float64(h)*c.screenScale)
	case driver.VUpward:
		op.GeoM.Scale(c.screenScale, c.screenScale)
	default:
		panic(fmt.Sprintf("ebiten: invalid v-direction: %d", vd))
	}

	op.GeoM.Translate(c.offsetX, c.offsetY)
	op.CompositeMode = CompositeModeCopy

	// filterScreen works with >=1 scale, but does not well with <1 scale.
	// Use regular FilterLinear instead so far (#669).
	if c.screenScale >= 1 {
		op.Filter = filterScreen
	} else {
		op.Filter = FilterLinear
	}
	_ = c.screen.DrawImage(c.offscreen, op)

	if err := buffered.EndFrame(); err != nil {
		return err
	}
	return nil
}

func (c *uiContext) AdjustPosition(x, y float64) (float64, float64) {
	d := uiDriver().DeviceScaleFactor()
	return (x*d - c.offsetX) / c.screenScale, (y*d - c.offsetY) / c.screenScale
}
<|endoftext|>


<commit_before>// Copyright 2014 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 boomer provides commands to run load tests and display results.
package boomer

import (
	"crypto/tls"
	"github.com/valyala/fasthttp"
	"net/url"
	"os"
	"os/signal"
	"sync"
	"time"

	"github.com/sschepens/pb"
)

var client *fasthttp.Client

type result struct {
	err           error
	statusCode    int
	duration      time.Duration
	contentLength int
}

type Boomer struct {
	// Request is the request to be made.
	Request *fasthttp.Request

	// N is the total number of requests to make.
	N int

	// C is the concurrency level, the number of concurrent workers to run.
	C int

	// Timeout in seconds.
	Timeout time.Duration

	// Qps is the rate limit.
	Qps int

	// AllowInsecure is an option to allow insecure TLS/SSL certificates.
	AllowInsecure bool

	// Output represents the output type. If "csv" is provided, the
	// output will be dumped as a csv stream.
	Output string

	// ProxyAddr is the address of HTTP proxy server in the format on "host:port".
	// Optional.
	ProxyAddr *url.URL

	// ReadAll determines whether the body of the response needs
	// to be fully consumed.
	ReadAll bool

	bar     *pb.ProgressBar
	results chan *result
	stop    chan struct{}
}

func (b *Boomer) startProgress() {
	if b.Output != "" {
		return
	}
	b.bar = pb.New(b.N)
	b.bar.Format("Bom !")
	b.bar.BarStart = "Pl"
	b.bar.BarEnd = "!"
	b.bar.Empty = " "
	b.bar.Current = "a"
	b.bar.CurrentN = "a"
	b.bar.Start()
}

func (b *Boomer) finalizeProgress() {
	if b.Output != "" {
		return
	}
	b.bar.Finish()
}

func (b *Boomer) incProgress() {
	if b.Output != "" {
		return
	}
	b.bar.Increment()
}

// Run makes all the requests, prints the summary. It blocks until
// all work is done.
func (b *Boomer) Run() {
	b.results = make(chan *result, b.C)
	b.stop = make(chan struct{})
	b.startProgress()

	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)

	go func() {
		<-c
		b.finalizeProgress()
		close(b.stop)
	}()

	r := newReport(b.N, b.results, b.Output)
	b.runWorkers()
	close(b.results)
	b.finalizeProgress()
	r.finalize()
}

func (b *Boomer) runWorker(wg *sync.WaitGroup, ch chan struct{}) {
	resp := fasthttp.AcquireResponse()
	req := fasthttp.AcquireRequest()
	b.Request.CopyTo(req)
	for range ch {
		s := time.Now()

		var code int
		var size int

		resp.Reset()
		var err error
		if b.Timeout > 0 {
			err = client.DoTimeout(req, resp, b.Timeout)
		} else {
			err = client.Do(req, resp)
		}
		if err == nil {
			size = resp.Header.ContentLength()
			code = resp.Header.StatusCode()
		}

		if b.ReadAll {
			resp.Body()
		}

		b.incProgress()
		b.results <- &result{
			statusCode:    code,
			duration:      time.Now().Sub(s),
			err:           err,
			contentLength: size,
		}
	}
	fasthttp.ReleaseResponse(resp)
	fasthttp.ReleaseRequest(req)
	wg.Done()
}

func (b *Boomer) runWorkers() {
	client = &fasthttp.Client{
		TLSConfig: &tls.Config{
			InsecureSkipVerify: b.AllowInsecure,
		},
		MaxConnsPerHost: b.C * 2,
	}
	var wg sync.WaitGroup
	wg.Add(b.C)

	var throttle <-chan time.Time
	if b.Qps > 0 {
		throttle = time.Tick(time.Duration(1e6/(b.Qps)) * time.Microsecond)
	}

	jobsch := make(chan struct{}, b.C)
	for i := 0; i < b.C; i++ {
		go b.runWorker(&wg, jobsch)
	}

Loop:
	for i := 0; i < b.N; i++ {
		if b.Qps > 0 {
			<-throttle
		}
		select {
		case <-b.stop:
			break Loop
		case jobsch <- struct{}{}:
			continue
		}
	}
	close(jobsch)
	wg.Wait()
}

// cloneRequest returns a clone of the provided *http.Request.
// The clone is a shallow copy of the struct and its Header map.
func cloneRequest(r *fasthttp.Request) *fasthttp.Request {
	req := fasthttp.AcquireRequest()
	r.CopyTo(req)
	return req
}
<commit_msg>Wait up to 10 seconds for requests to finish on interrupt<commit_after>// Copyright 2014 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 boomer provides commands to run load tests and display results.
package boomer

import (
	"crypto/tls"
	"github.com/valyala/fasthttp"
	"net/url"
	"os"
	"os/signal"
	"sync"
	"time"

	"github.com/sschepens/pb"
)

var client *fasthttp.Client

type result struct {
	err           error
	statusCode    int
	duration      time.Duration
	contentLength int
}

type Boomer struct {
	// Request is the request to be made.
	Request *fasthttp.Request

	// N is the total number of requests to make.
	N int

	// C is the concurrency level, the number of concurrent workers to run.
	C int

	// Timeout in seconds.
	Timeout time.Duration

	// Qps is the rate limit.
	Qps int

	// AllowInsecure is an option to allow insecure TLS/SSL certificates.
	AllowInsecure bool

	// Output represents the output type. If "csv" is provided, the
	// output will be dumped as a csv stream.
	Output string

	// ProxyAddr is the address of HTTP proxy server in the format on "host:port".
	// Optional.
	ProxyAddr *url.URL

	// ReadAll determines whether the body of the response needs
	// to be fully consumed.
	ReadAll bool

	bar     *pb.ProgressBar
	results chan *result
	stop    chan struct{}
}

func (b *Boomer) startProgress() {
	if b.Output != "" {
		return
	}
	b.bar = pb.New(b.N)
	b.bar.Format("Bom !")
	b.bar.BarStart = "Pl"
	b.bar.BarEnd = "!"
	b.bar.Empty = " "
	b.bar.Current = "a"
	b.bar.CurrentN = "a"
	b.bar.Start()
}

func (b *Boomer) finalizeProgress() {
	if b.Output != "" {
		return
	}
	b.bar.Finish()
}

func (b *Boomer) incProgress() {
	if b.Output != "" {
		return
	}
	b.bar.Increment()
}

// Run makes all the requests, prints the summary. It blocks until
// all work is done.
func (b *Boomer) Run() {
	var shutdownTimer *time.Timer
	b.results = make(chan *result, b.C)
	b.stop = make(chan struct{})
	b.startProgress()

	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)

	go func() {
		<-c
		shutdownTimer = time.AfterFunc(10*time.Second, func() {
			os.Exit(1)
		})
		b.finalizeProgress()
		close(b.stop)
	}()

	r := newReport(b.N, b.results, b.Output)
	b.runWorkers()
	if shutdownTimer != nil {
		shutdownTimer.Stop()
	}
	close(b.results)
	b.finalizeProgress()
	r.finalize()
}

func (b *Boomer) runWorker(wg *sync.WaitGroup, ch chan struct{}) {
	resp := fasthttp.AcquireResponse()
	req := fasthttp.AcquireRequest()
	b.Request.CopyTo(req)
	for range ch {
		s := time.Now()

		var code int
		var size int

		resp.Reset()
		var err error
		if b.Timeout > 0 {
			err = client.DoTimeout(req, resp, b.Timeout)
		} else {
			err = client.Do(req, resp)
		}
		if err == nil {
			size = resp.Header.ContentLength()
			code = resp.Header.StatusCode()
		}

		if b.ReadAll {
			resp.Body()
		}

		b.incProgress()
		b.results <- &result{
			statusCode:    code,
			duration:      time.Now().Sub(s),
			err:           err,
			contentLength: size,
		}
	}
	fasthttp.ReleaseResponse(resp)
	fasthttp.ReleaseRequest(req)
	wg.Done()
}

func (b *Boomer) runWorkers() {
	client = &fasthttp.Client{
		TLSConfig: &tls.Config{
			InsecureSkipVerify: b.AllowInsecure,
		},
		MaxConnsPerHost: b.C * 2,
	}
	var wg sync.WaitGroup
	wg.Add(b.C)

	var throttle <-chan time.Time
	if b.Qps > 0 {
		throttle = time.Tick(time.Duration(1e6/(b.Qps)) * time.Microsecond)
	}

	jobsch := make(chan struct{}, b.C)
	for i := 0; i < b.C; i++ {
		go b.runWorker(&wg, jobsch)
	}

Loop:
	for i := 0; i < b.N; i++ {
		if b.Qps > 0 {
			<-throttle
		}
		select {
		case <-b.stop:
			break Loop
		case jobsch <- struct{}{}:
			continue
		}
	}
	close(jobsch)
	wg.Wait()
}

// cloneRequest returns a clone of the provided *http.Request.
// The clone is a shallow copy of the struct and its Header map.
func cloneRequest(r *fasthttp.Request) *fasthttp.Request {
	req := fasthttp.AcquireRequest()
	r.CopyTo(req)
	return req
}
<|endoftext|>


<commit_before>package main

import (
	"os"
)

func file(name string, create bool) (*os.File, error) {
	switch name {
	case "stdin":
		return os.Stdin, nil
	case "stdout":
		return os.Stdout, nil
	default:
		if create {
			return os.Create(name)
		}
		return os.Open(name)
	}
}
<commit_msg>vegeta: Helper decoder function<commit_after>package main

import (
	"errors"
	"fmt"
	"io"
	"os"
	"strings"

	vegeta "github.com/tsenart/vegeta/lib"
)

func file(name string, create bool) (*os.File, error) {
	switch name {
	case "stdin":
		return os.Stdin, nil
	case "stdout":
		return os.Stdout, nil
	default:
		if create {
			return os.Create(name)
		}
		return os.Open(name)
	}
}

func decoder(files []string) (vegeta.Decoder, io.Closer, error) {
	closer := make(multiCloser, 0, len(files))
	decs := make([]vegeta.Decoder, 0, len(files))
	for _, f := range files {
		rc, err := file(f, false)
		if err != nil {
			return nil, closer, err
		}

		dec := vegeta.DecoderFor(rc)
		if dec == nil {
			return nil, closer, fmt.Errorf("encode: can't detect encoding of %q", f)
		}

		decs = append(decs, dec)
		closer = append(closer, rc)
	}
	return vegeta.NewRoundRobinDecoder(decs...), closer, nil
}

type multiCloser []io.Closer

func (mc multiCloser) Close() error {
	var errs []string
	for _, c := range mc {
		if err := c.Close(); err != nil {
			errs = append(errs, err.Error())
		}
	}

	if len(errs) > 0 {
		return errors.New(strings.Join(errs, "; "))
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"runtime"
	"time"

	"github.com/urfave/cli"
)

var (
	DataChan  chan map[string]string
	FillerSig chan bool
)

type Dirstate struct {
	Location string
	Contents map[string][]string
}

type Crossover struct {
	Source   string
	Dest     string
	Contents map[string][]string
}

func Filler() {
	for {
		select {
		case msg := <-DataChan:
			Debug <- fmt.Sprintf("DATA %+v", msg["Source"])
			Debug <- fmt.Sprintf("DATA %+v", msg["Destination"])
		case <-FillerSig:
			FillerSig <- true
			return
		}
	}
}

func BackFill(c *cli.Context) {
	if len(c.Args()) < 2 {
		Error <- "Invalid arguments. See 'flustro help fill' for more information."
		os.Exit(1)
	} else {
		// declare our variables
		var srcObj interface{}
		var dstObj interface{}

		// let's get our source object
		srcDir := c.Args().Get(0)
		if isDir(srcDir) {
			srcObj = ListDir(srcDir)
		} else if isFile(srcDir) {
			srcObj = srcDir
		} else {
			Error <- fmt.Sprintf("%s is not a file or directory.", srcDir)
		}

		// and our dest object
		dstDir := c.Args().Get(1)
		if isDir(dstDir) {
			dstObj = ListDir(fmt.Sprintf("%s", dstDir))
		} else if isFile(dstDir) {
			dstObj = fmt.Sprintf("%s", dstDir)
		} else {
			Error <- fmt.Sprintf("%s is not a file or directory.", dstDir)
		}

		// Now let's spawn our pool of workers if our args are both directories
		for i := 0; i < c.Int("j"); i++ {
			go Filler()
		}
		// next we'll start processing through our srcObj and dstObj lists and
		// backfill everything that's present in both locations
		// for k, _ := range srcObj.Contents {

		// or simply spawn a single Filler and let it do it's job if both args are files
		go Filler()
		data := map[string]string{"Source": fmt.Sprintf("%+v", srcObj), "Destination": fmt.Sprintf("%+v", dstObj)}
		DataChan <- data
		time.Sleep(1 * time.Second)
	}
	return
}

/*
import (
	"errors"
	"fmt"
	"os"
	"path"
	"runtime"
	"sync"
	"time"

	"github.com/dustin/go-humanize"
	"github.com/fuzzy/gcl"
	"github.com/kisielk/whisper-go/whisper"
	"github.com/urfave/cli"
)

type FillState struct {
	StartTime int64
	Count     int64
	CountLock sync.Mutex
	CountChan chan bool
	SrcTotal  int64
	DstTotal  int64
	Overlap   int64
}

func (f FillState) Increment() {
	f.CountLock.Lock()
	f.Count++
	f.CountChan <- true
	f.CountLock.Unlock()
}

func (f FillState) DumpState() {
	// grab our lock, this ensures accurate reporting, and gives a bit more
	// of a concurrency throttle. This is not necessarily a bad thing.
	f.CountLock.Lock()
	r := (time.Now().Unix() - f.StartTime)
	c := f.Count
	if r > 0 {
		c = (f.Count / r)
	}
	p := (float64(f.Count) / float64(f.Overlap)) * 100.00
	fmt.Printf("%s %-6s/%-6s (%6.02f%%) in %d seconds @ %-6s/sec",
		gcl.String("Info").Green().Bold(),
		humanize.Comma(f.Count),
		humanize.Comma(f.Overlap),
		p, HumanTime(int(r)), c)
	f.CountLock.Unlock()
}

var (
	State FillState
)

func stateWorker(t chan bool) {
	for {
		select {
		case sig := <-State.CountChan:
			if sig {
				State.DumpState()
			} else {
				return
			}
		case <-t:
			return
		}
	}
}

func fillWorker(d chan map[string]string, t chan bool) {
	for {
		msg := <-d
		if msg["SRC"] == "__EXIT__" && msg["DST"] == "__EXIT__" {
			return
		} else {
			if isFile(msg["SRC"]) && isFile(msg["DST"]) {
				if !backfillFile(msg["SRC"], msg["DST"]) {
					Error.Printf("S:%s D:%s - Backfill operation failed.",
						msg["SRC"],
						msg["DST"])
					os.Exit(1)
				} else {
					State.Increment()
				}
			}
		}
	}
}

func backfillFile(s string, d string) bool {
	// Open our filehandles
	sDb, sErr := whisper.Open(s)
	dDb, dErr := whisper.Open(d)
	if !chkErr(sErr) || !chkErr(dErr) {
		os.Exit(1)
	}
	// Defer their closings
	defer sDb.Close()
	defer dDb.Close()

	// Now for a series of checks, first to ensure that both
	// files have the same number of archives in them.
	if sDb.Header.Metadata.ArchiveCount != dDb.Header.Metadata.ArchiveCount {
		Error.Println("The files have a mismatched set of archives.")
		return false
	}

	// Now we'll start processing the archives, checking as we go to see if they
	// are matched. That way we at least fill in what we can, possibly....
	for i, a := range sDb.Header.Archives {
		// The offset
		if a.Offset == dDb.Header.Archives[i].Offset {
			// and the number of points
			if a.Points == dDb.Header.Archives[i].Points {
				// and finally the interval
				if a.SecondsPerPoint == dDb.Header.Archives[i].SecondsPerPoint {
					// ok, now let's get rolling through the archives
					sp, se := sDb.DumpArchive(i)
					if se != nil {
						Error.Println(se.Error())
						os.Exit(1)
					}
					dp, de := dDb.DumpArchive(i)
					if de != nil {
						Error.Println(de.Error())
						os.Exit(1)
					}
					for idx := 0; idx < len(sp); idx++ {
						if sp[idx].Timestamp != 0 && sp[idx].Value != 0 {
							if dp[idx].Timestamp == 0 || dp[idx].Value == 0 {
								dp[idx].Timestamp = sp[idx].Timestamp
								dp[idx].Value = sp[idx].Value
								dDb.Update(dp[idx])
							}
						}
					}
				}
			}
		}
	}
	return true
}

func Filler(c *cli.Context) error {
	if len(c.Args()) == 2 {
		args := c.Args()
		if isDir(args[0]) && isDir(args[1]) {
			State.Count = 0

			// First things first, let's spawn our pool of workers.
			dataCh := make(chan map[string]string, 1)
			timeout := make(chan bool, 30)
			go stateWorker(timeout)
			for i := 0; i < c.Int("j"); i++ {
				go fillWorker(dataCh, timeout)
			}

			// Let's get this dir walking in there then shall we?
			srcFiles := listFiles(args[0])
			dstFiles := listFiles(args[1])

			// Now let's find all our overlap
			overlap := []string{}
			for a := 0; a < len(srcFiles); a++ {
				for b := 0; b < len(dstFiles); b++ {
					if srcFiles[a] == dstFiles[b] {
						overlap = append(overlap, srcFiles[a])
					}
				}
			}

			// And display some stats
			Info.Printf("srcDir: %d files, dstDir: %d files, %d overlap.",
				len(srcFiles),
				len(dstFiles),
				len(overlap))
			State.Overlap = int64(len(overlap))
			State.SrcTotal = int64(len(srcFiles))
			State.DstTotal = int64(len(dstFiles))

			// Now we can push in all our data, and let our workers do their
			// lovely little thing. Ahhhhh concurrency.
			StartTime = time.Now().Unix()
			for i := 0; i < len(overlap); i++ {
				dataCh <- map[string]string{
					"SRC": fmt.Sprintf("%s/%s", args[0], overlap[i]),
					"DST": fmt.Sprintf("%s/%s", args[1], overlap[i]),
				}
			}

			// And while they're off doing that, here we will just sit and watch
			// the return channel and count things. Once we have all our
			// backfill operations accounted for, we can reap all of our workers
			// and carry on.
			for State.Count < int64(len(overlap)) {
				time.Sleep(1 * time.Second)
			}
			// And finally let's reap all our children
			for idx := 0; idx < c.Int("j"); idx++ {
				dataCh <- map[string]string{
					"SRC": "__EXIT__",
					"DST": "__EXIT__",
				}
			}

			fmt.Println("")
			Info.Println((State.Overlap / (time.Now().Unix() - StartTime)),
				"whisper files processed per second.")
		} else {
			if !backfillFile(args[0], args[1]) {
				e := fmt.Sprintf("Error while backfilling.")
				Error.Println(e)
				return errors.New(e)
			}
		}
	} else {
		var e string
		e = fmt.Sprintf("Wrong number of paramters given.")
		Error.Println(e)
		Error.Printf("Try '%s help fill' for more information",
			path.Base(os.Args[0]))
		return errors.New(e)
	}
	return nil
}
*/
func init() {
	DataChan = make(chan map[string]string)
	Commands = append(Commands, cli.Command{
		Name:        "fill",
		Aliases:     []string{"f"},
		Usage:       "Backfill datapoints in the dst from the src",
		Description: "Backfill datapoints in the dst from the src",
		ArgsUsage:   "<src(File|Dir)> <dst(File|Dir)>",
		Flags: []cli.Flag{
			cli.IntFlag{
				Name:  "j",
				Usage: "Number of workers (for directory recursion)",
				Value: runtime.GOMAXPROCS(0),
			},
		},
		SkipFlagParsing: false,
		HideHelp:        false,
		Hidden:          false,
		Action:          BackFill,
	})
}
<commit_msg>proper argument/file/dir detection and error handling<commit_after>package main

import (
	"fmt"
	"os"
	"runtime"
	"time"

	"github.com/urfave/cli"
)

var (
	DataChan  chan map[string]string
	FillerSig chan bool
)

type Dirstate struct {
	Location string
	Contents map[string][]string
}

func Filler() {
	for {
		select {
		case msg := <-DataChan:
			Debug <- fmt.Sprintf("Backfill: %s -> %s", msg["Source"], msg["Destination"])
		case <-FillerSig:
			FillerSig <- true
			return
		}
	}
}

func BackFill(c *cli.Context) {
	if len(c.Args()) < 2 {
		Error <- "Invalid arguments. See 'flustro help fill' for more information."
		os.Exit(1)
	} else {
		// declare our variables
		var srcObj Dirstate
		var dstObj Dirstate
		srcDir := c.Args().Get(0)
		dstDir := c.Args().Get(1)

		// Now let's do the heavy lifting
		if isDir(srcDir) && isDir(dstDir) {
			// First let's get our dir contents
			srcObj = ListDir(srcDir)
			dstObj = ListDir(dstDir)
			// then spawn our worker pool, and get to processing
			for i := 0; i < c.Int("j"); i++ {
				go Filler()
			}
			// next we'll start processing through our srcObj and dstObj lists and
			// backfill everything that's present in both locations
			for k, _ := range srcObj.Contents {
				if _, ok := dstObj.Contents[k]; ok {
					for _, v := range srcObj.Contents[k] {
						for _, dv := range dstObj.Contents[k] {
							if v == dv {
								DataChan <- map[string]string{
									"Source":      fmt.Sprintf("%s/%s/%s", srcObj.Location, k, v),
									"Destination": fmt.Sprintf("%s/%s/%s", dstObj.Location, k, v),
								}
							}
						}
					}
				}
			}
		} else if isFile(srcDir) && isFile(dstDir) {
			// we only need one worker for this job
			go Filler()
			DataChan <- map[string]string{
				"Source":      srcDir,
				"Destination": dstDir,
			}
		} else {
			Error <- fmt.Sprintf("SRC and DST must be either both files or both dirs.")
		}

		time.Sleep(500 * time.Millisecond)
	}
	return
}

/*
import (
	"errors"
	"fmt"
	"os"
	"path"
	"runtime"
	"sync"
	"time"

	"github.com/dustin/go-humanize"
	"github.com/fuzzy/gcl"
	"github.com/kisielk/whisper-go/whisper"
	"github.com/urfave/cli"
)

type FillState struct {
	StartTime int64
	Count     int64
	CountLock sync.Mutex
	CountChan chan bool
	SrcTotal  int64
	DstTotal  int64
	Overlap   int64
}

func (f FillState) Increment() {
	f.CountLock.Lock()
	f.Count++
	f.CountChan <- true
	f.CountLock.Unlock()
}

func (f FillState) DumpState() {
	// grab our lock, this ensures accurate reporting, and gives a bit more
	// of a concurrency throttle. This is not necessarily a bad thing.
	f.CountLock.Lock()
	r := (time.Now().Unix() - f.StartTime)
	c := f.Count
	if r > 0 {
		c = (f.Count / r)
	}
	p := (float64(f.Count) / float64(f.Overlap)) * 100.00
	fmt.Printf("%s %-6s/%-6s (%6.02f%%) in %d seconds @ %-6s/sec",
		gcl.String("Info").Green().Bold(),
		humanize.Comma(f.Count),
		humanize.Comma(f.Overlap),
		p, HumanTime(int(r)), c)
	f.CountLock.Unlock()
}

var (
	State FillState
)

func stateWorker(t chan bool) {
	for {
		select {
		case sig := <-State.CountChan:
			if sig {
				State.DumpState()
			} else {
				return
			}
		case <-t:
			return
		}
	}
}

func fillWorker(d chan map[string]string, t chan bool) {
	for {
		msg := <-d
		if msg["SRC"] == "__EXIT__" && msg["DST"] == "__EXIT__" {
			return
		} else {
			if isFile(msg["SRC"]) && isFile(msg["DST"]) {
				if !backfillFile(msg["SRC"], msg["DST"]) {
					Error.Printf("S:%s D:%s - Backfill operation failed.",
						msg["SRC"],
						msg["DST"])
					os.Exit(1)
				} else {
					State.Increment()
				}
			}
		}
	}
}

func backfillFile(s string, d string) bool {
	// Open our filehandles
	sDb, sErr := whisper.Open(s)
	dDb, dErr := whisper.Open(d)
	if !chkErr(sErr) || !chkErr(dErr) {
		os.Exit(1)
	}
	// Defer their closings
	defer sDb.Close()
	defer dDb.Close()

	// Now for a series of checks, first to ensure that both
	// files have the same number of archives in them.
	if sDb.Header.Metadata.ArchiveCount != dDb.Header.Metadata.ArchiveCount {
		Error.Println("The files have a mismatched set of archives.")
		return false
	}

	// Now we'll start processing the archives, checking as we go to see if they
	// are matched. That way we at least fill in what we can, possibly....
	for i, a := range sDb.Header.Archives {
		// The offset
		if a.Offset == dDb.Header.Archives[i].Offset {
			// and the number of points
			if a.Points == dDb.Header.Archives[i].Points {
				// and finally the interval
				if a.SecondsPerPoint == dDb.Header.Archives[i].SecondsPerPoint {
					// ok, now let's get rolling through the archives
					sp, se := sDb.DumpArchive(i)
					if se != nil {
						Error.Println(se.Error())
						os.Exit(1)
					}
					dp, de := dDb.DumpArchive(i)
					if de != nil {
						Error.Println(de.Error())
						os.Exit(1)
					}
					for idx := 0; idx < len(sp); idx++ {
						if sp[idx].Timestamp != 0 && sp[idx].Value != 0 {
							if dp[idx].Timestamp == 0 || dp[idx].Value == 0 {
								dp[idx].Timestamp = sp[idx].Timestamp
								dp[idx].Value = sp[idx].Value
								dDb.Update(dp[idx])
							}
						}
					}
				}
			}
		}
	}
	return true
}

func Filler(c *cli.Context) error {
	if len(c.Args()) == 2 {
		args := c.Args()
		if isDir(args[0]) && isDir(args[1]) {
			State.Count = 0

			// First things first, let's spawn our pool of workers.
			dataCh := make(chan map[string]string, 1)
			timeout := make(chan bool, 30)
			go stateWorker(timeout)
			for i := 0; i < c.Int("j"); i++ {
				go fillWorker(dataCh, timeout)
			}

			// Let's get this dir walking in there then shall we?
			srcFiles := listFiles(args[0])
			dstFiles := listFiles(args[1])

			// Now let's find all our overlap
			overlap := []string{}
			for a := 0; a < len(srcFiles); a++ {
				for b := 0; b < len(dstFiles); b++ {
					if srcFiles[a] == dstFiles[b] {
						overlap = append(overlap, srcFiles[a])
					}
				}
			}

			// And display some stats
			Info.Printf("srcDir: %d files, dstDir: %d files, %d overlap.",
				len(srcFiles),
				len(dstFiles),
				len(overlap))
			State.Overlap = int64(len(overlap))
			State.SrcTotal = int64(len(srcFiles))
			State.DstTotal = int64(len(dstFiles))

			// Now we can push in all our data, and let our workers do their
			// lovely little thing. Ahhhhh concurrency.
			StartTime = time.Now().Unix()
			for i := 0; i < len(overlap); i++ {
				dataCh <- map[string]string{
					"SRC": fmt.Sprintf("%s/%s", args[0], overlap[i]),
					"DST": fmt.Sprintf("%s/%s", args[1], overlap[i]),
				}
			}

			// And while they're off doing that, here we will just sit and watch
			// the return channel and count things. Once we have all our
			// backfill operations accounted for, we can reap all of our workers
			// and carry on.
			for State.Count < int64(len(overlap)) {
				time.Sleep(1 * time.Second)
			}
			// And finally let's reap all our children
			for idx := 0; idx < c.Int("j"); idx++ {
				dataCh <- map[string]string{
					"SRC": "__EXIT__",
					"DST": "__EXIT__",
				}
			}

			fmt.Println("")
			Info.Println((State.Overlap / (time.Now().Unix() - StartTime)),
				"whisper files processed per second.")
		} else {
			if !backfillFile(args[0], args[1]) {
				e := fmt.Sprintf("Error while backfilling.")
				Error.Println(e)
				return errors.New(e)
			}
		}
	} else {
		var e string
		e = fmt.Sprintf("Wrong number of paramters given.")
		Error.Println(e)
		Error.Printf("Try '%s help fill' for more information",
			path.Base(os.Args[0]))
		return errors.New(e)
	}
	return nil
}
*/
func init() {
	DataChan = make(chan map[string]string)
	Commands = append(Commands, cli.Command{
		Name:        "fill",
		Aliases:     []string{"f"},
		Usage:       "Backfill datapoints in the dst from the src",
		Description: "Backfill datapoints in the dst from the src",
		ArgsUsage:   "<src(File|Dir)> <dst(File|Dir)>",
		Flags: []cli.Flag{
			cli.IntFlag{
				Name:  "j",
				Usage: "Number of workers (for directory recursion)",
				Value: runtime.GOMAXPROCS(0),
			},
		},
		SkipFlagParsing: false,
		HideHelp:        false,
		Hidden:          false,
		Action:          BackFill,
	})
}
<|endoftext|>


<commit_before>package cli

import (
	"bytes"
	"fmt"
	"io"
	"strings"
	"text/template"
)

// ToFishCompletion creates a fish completion string for the `*App`
// The function errors if either parsing or writing of the string fails.
func (a *App) ToFishCompletion() (string, error) {
	var w bytes.Buffer
	if err := a.writeFishCompletionTemplate(&w); err != nil {
		return "", err
	}
	return w.String(), nil
}

type fishCompletionTemplate struct {
	App         *App
	Completions []string
	AllCommands []string
}

func (a *App) writeFishCompletionTemplate(w io.Writer) error {
	const name = "cli"
	t, err := template.New(name).Parse(FishCompletionTemplate)
	if err != nil {
		return err
	}
	allCommands := []string{}

	// Add global flags
	completions := a.prepareFishFlags(a.VisibleFlags(), allCommands)

	// Add help flag
	if !a.HideHelp {
		completions = append(
			completions,
			a.prepareFishFlags([]Flag{HelpFlag}, allCommands)...,
		)
	}

	// Add version flag
	if !a.HideVersion {
		completions = append(
			completions,
			a.prepareFishFlags([]Flag{VersionFlag}, allCommands)...,
		)
	}

	// Add commands and their flags
	completions = append(
		completions,
		a.prepareFishCommands(a.VisibleCommands(), &allCommands, []string{})...,
	)

	return t.ExecuteTemplate(w, name, &fishCompletionTemplate{
		App:         a,
		Completions: completions,
		AllCommands: allCommands,
	})
}

func (a *App) prepareFishCommands(
	commands []Command,
	allCommands *[]string,
	previousCommands []string,
) []string {
	completions := []string{}
	for i := range commands {
		command := &commands[i]

		var completion strings.Builder
		completion.WriteString(fmt.Sprintf(
			"complete -r -c %s -n '%s' -a '%s'",
			a.Name,
			a.fishSubcommandHelper(previousCommands),
			strings.Join(command.Names(), " "),
		))

		if command.Usage != "" {
			completion.WriteString(fmt.Sprintf(" -d '%s'", command.Usage))
		}

		if !command.HideHelp {
			completions = append(
				completions,
				a.prepareFishFlags([]Flag{HelpFlag}, command.Names())...,
			)
		}

		*allCommands = append(*allCommands, command.Names()...)
		completions = append(completions, completion.String())
		completions = append(
			completions,
			a.prepareFishFlags(command.Flags, command.Names())...,
		)

		// recursevly iterate subcommands
		if len(command.Subcommands) > 0 {
			completions = append(
				completions,
				a.prepareFishCommands(
					command.Subcommands, allCommands, command.Names(),
				)...,
			)
		}
	}

	return completions
}

func (a *App) prepareFishFlags(
	flags []Flag,
	previousCommands []string,
) []string {
	completions := []string{}
	for _, f := range flags {
		flag, ok := f.(DocGenerationFlag)
		if !ok {
			continue
		}

		completion := &strings.Builder{}
		completion.WriteString(fmt.Sprintf(
			"complete -c %s -n '%s'",
			a.Name,
			a.fishSubcommandHelper(previousCommands),
		))

		fishAddFileFlag(f, completion)

		for idx, opt := range strings.Split(flag.GetName(), ",") {
			if idx == 0 {
				completion.WriteString(fmt.Sprintf(
					" -l %s", strings.TrimSpace(opt),
				))
			} else {
				completion.WriteString(fmt.Sprintf(
					" -s %s", strings.TrimSpace(opt),
				))

			}
		}

		if flag.TakesValue() {
			completion.WriteString(" -r")
		}

		if flag.GetUsage() != "" {
			completion.WriteString(fmt.Sprintf(" -d '%s'", flag.GetUsage()))
		}

		completions = append(completions, completion.String())
	}

	return completions
}

func fishAddFileFlag(
	flag Flag,
	completion *strings.Builder,
) {
	switch f := flag.(type) {
	case GenericFlag:
		if f.TakesFile {
			return
		}
	case StringFlag:
		if f.TakesFile {
			return
		}
	case StringSliceFlag:
		if f.TakesFile {
			return
		}
	}
	completion.WriteString(" -f")
}

func (a *App) fishSubcommandHelper(allCommands []string) string {
	fishHelper := fmt.Sprintf("__fish_%s_no_subcommand", a.Name)
	if len(allCommands) > 0 {
		fishHelper = fmt.Sprintf(
			"__fish_seen_subcommand_from %s",
			strings.Join(allCommands, " "),
		)
	}
	return fishHelper

}
<commit_msg>Update function alignment<commit_after>package cli

import (
	"bytes"
	"fmt"
	"io"
	"strings"
	"text/template"
)

// ToFishCompletion creates a fish completion string for the `*App`
// The function errors if either parsing or writing of the string fails.
func (a *App) ToFishCompletion() (string, error) {
	var w bytes.Buffer
	if err := a.writeFishCompletionTemplate(&w); err != nil {
		return "", err
	}
	return w.String(), nil
}

type fishCompletionTemplate struct {
	App         *App
	Completions []string
	AllCommands []string
}

func (a *App) writeFishCompletionTemplate(w io.Writer) error {
	const name = "cli"
	t, err := template.New(name).Parse(FishCompletionTemplate)
	if err != nil {
		return err
	}
	allCommands := []string{}

	// Add global flags
	completions := a.prepareFishFlags(a.VisibleFlags(), allCommands)

	// Add help flag
	if !a.HideHelp {
		completions = append(
			completions,
			a.prepareFishFlags([]Flag{HelpFlag}, allCommands)...,
		)
	}

	// Add version flag
	if !a.HideVersion {
		completions = append(
			completions,
			a.prepareFishFlags([]Flag{VersionFlag}, allCommands)...,
		)
	}

	// Add commands and their flags
	completions = append(
		completions,
		a.prepareFishCommands(a.VisibleCommands(), &allCommands, []string{})...,
	)

	return t.ExecuteTemplate(w, name, &fishCompletionTemplate{
		App:         a,
		Completions: completions,
		AllCommands: allCommands,
	})
}

func (a *App) prepareFishCommands(commands []Command, allCommands *[]string, previousCommands []string) []string {
	completions := []string{}
	for i := range commands {
		command := &commands[i]

		var completion strings.Builder
		completion.WriteString(fmt.Sprintf(
			"complete -r -c %s -n '%s' -a '%s'",
			a.Name,
			a.fishSubcommandHelper(previousCommands),
			strings.Join(command.Names(), " "),
		))

		if command.Usage != "" {
			completion.WriteString(fmt.Sprintf(" -d '%s'", command.Usage))
		}

		if !command.HideHelp {
			completions = append(
				completions,
				a.prepareFishFlags([]Flag{HelpFlag}, command.Names())...,
			)
		}

		*allCommands = append(*allCommands, command.Names()...)
		completions = append(completions, completion.String())
		completions = append(
			completions,
			a.prepareFishFlags(command.Flags, command.Names())...,
		)

		// recursevly iterate subcommands
		if len(command.Subcommands) > 0 {
			completions = append(
				completions,
				a.prepareFishCommands(
					command.Subcommands, allCommands, command.Names(),
				)...,
			)
		}
	}

	return completions
}

func (a *App) prepareFishFlags(flags []Flag, previousCommands []string) []string {
	completions := []string{}
	for _, f := range flags {
		flag, ok := f.(DocGenerationFlag)
		if !ok {
			continue
		}

		completion := &strings.Builder{}
		completion.WriteString(fmt.Sprintf(
			"complete -c %s -n '%s'",
			a.Name,
			a.fishSubcommandHelper(previousCommands),
		))

		fishAddFileFlag(f, completion)

		for idx, opt := range strings.Split(flag.GetName(), ",") {
			if idx == 0 {
				completion.WriteString(fmt.Sprintf(
					" -l %s", strings.TrimSpace(opt),
				))
			} else {
				completion.WriteString(fmt.Sprintf(
					" -s %s", strings.TrimSpace(opt),
				))

			}
		}

		if flag.TakesValue() {
			completion.WriteString(" -r")
		}

		if flag.GetUsage() != "" {
			completion.WriteString(fmt.Sprintf(" -d '%s'", flag.GetUsage()))
		}

		completions = append(completions, completion.String())
	}

	return completions
}

func fishAddFileFlag(flag Flag, completion *strings.Builder) {
	switch f := flag.(type) {
	case GenericFlag:
		if f.TakesFile {
			return
		}
	case StringFlag:
		if f.TakesFile {
			return
		}
	case StringSliceFlag:
		if f.TakesFile {
			return
		}
	}
	completion.WriteString(" -f")
}

func (a *App) fishSubcommandHelper(allCommands []string) string {
	fishHelper := fmt.Sprintf("__fish_%s_no_subcommand", a.Name)
	if len(allCommands) > 0 {
		fishHelper = fmt.Sprintf(
			"__fish_seen_subcommand_from %s",
			strings.Join(allCommands, " "),
		)
	}
	return fishHelper

}
<|endoftext|>


<commit_before>package butlerd

import (
	"context"
	"encoding/json"
	"fmt"
	"time"

	"github.com/itchio/wharf/werrors"

	"github.com/itchio/httpkit/neterr"
	"github.com/itchio/httpkit/progress"

	"crawshaw.io/sqlite"
	"github.com/itchio/butler/database/models"
	itchio "github.com/itchio/go-itchio"
	"github.com/itchio/wharf/state"
	"github.com/pkg/errors"
	"github.com/sourcegraph/jsonrpc2"
)

type RequestHandler func(rc *RequestContext) (interface{}, error)
type NotificationHandler func(rc *RequestContext)

type GetClientFunc func(key string) *itchio.Client

type Router struct {
	Handlers             map[string]RequestHandler
	NotificationHandlers map[string]NotificationHandler
	CancelFuncs          *CancelFuncs
	dbPool               *sqlite.Pool
	getClient            GetClientFunc

	ButlerVersion       string
	ButlerVersionString string
}

func NewRouter(dbPool *sqlite.Pool, getClient GetClientFunc) *Router {
	return &Router{
		Handlers:             make(map[string]RequestHandler),
		NotificationHandlers: make(map[string]NotificationHandler),
		CancelFuncs: &CancelFuncs{
			Funcs: make(map[string]context.CancelFunc),
		},
		dbPool:    dbPool,
		getClient: getClient,
	}
}

func (r *Router) Register(method string, rh RequestHandler) {
	if _, ok := r.Handlers[method]; ok {
		panic(fmt.Sprintf("Can't register handler twice for %s", method))
	}
	r.Handlers[method] = rh
}

func (r *Router) RegisterNotification(method string, nh NotificationHandler) {
	if _, ok := r.NotificationHandlers[method]; ok {
		panic(fmt.Sprintf("Can't register handler twice for %s", method))
	}
	r.NotificationHandlers[method] = nh
}

func (r *Router) Dispatch(ctx context.Context, origConn *jsonrpc2.Conn, req *jsonrpc2.Request) {
	method := req.Method
	var res interface{}

	conn := &JsonRPC2Conn{origConn}
	consumer, cErr := NewStateConsumer(&NewStateConsumerParams{
		Ctx:  ctx,
		Conn: conn,
	})
	if cErr != nil {
		return
	}

	err := func() (err error) {
		defer func() {
			if r := recover(); r != nil {
				if rErr, ok := r.(error); ok {
					err = errors.WithStack(rErr)
				} else {
					err = errors.Errorf("panic: %v", r)
				}
			}
		}()

		rc := &RequestContext{
			Ctx:         ctx,
			Consumer:    consumer,
			Params:      req.Params,
			Conn:        conn,
			CancelFuncs: r.CancelFuncs,
			dbPool:      r.dbPool,
			Client:      r.getClient,

			ButlerVersion:       r.ButlerVersion,
			ButlerVersionString: r.ButlerVersionString,
		}

		if req.Notif {
			if nh, ok := r.NotificationHandlers[req.Method]; ok {
				nh(rc)
			}
		} else {
			if h, ok := r.Handlers[method]; ok {
				rc.Consumer.OnProgress = func(alpha float64) {
					if rc.tracker == nil {
						// skip
						return
					}

					rc.tracker.SetProgress(alpha)
					notif := ProgressNotification{
						Progress: alpha,
						ETA:      rc.tracker.ETA().Seconds(),
						BPS:      rc.tracker.BPS(),
					}
					// cannot use autogenerated wrappers to avoid import cycles
					rc.Notify("Progress", notif)
				}
				rc.Consumer.OnProgressLabel = func(label string) {
					// muffin
				}
				rc.Consumer.OnPauseProgress = func() {
					if rc.tracker != nil {
						rc.tracker.Pause()
					}
				}
				rc.Consumer.OnResumeProgress = func() {
					if rc.tracker != nil {
						rc.tracker.Resume()
					}
				}

				res, err = h(rc)
			} else {
				err = &RpcError{
					Code:    jsonrpc2.CodeMethodNotFound,
					Message: fmt.Sprintf("Method '%s' not found", req.Method),
				}
			}
		}
		return
	}()

	if req.Notif {
		return
	}

	if err == nil {
		err = origConn.Reply(ctx, req.ID, res)
		if err != nil {
			consumer.Errorf("Error while replying: %s", err.Error())
		}
		return
	}

	var code int64
	var message string
	var data map[string]interface{}

	if ee, ok := AsButlerdError(err); ok {
		code = ee.RpcErrorCode()
		message = ee.RpcErrorMessage()
		data = ee.RpcErrorData()
	} else {
		if neterr.IsNetworkError(err) {
			code = int64(CodeNetworkDisconnected)
			message = CodeNetworkDisconnected.Error()
		} else if errors.Cause(err) == werrors.ErrCancelled {
			code = int64(CodeOperationCancelled)
			message = CodeOperationCancelled.Error()
		} else {
			code = jsonrpc2.CodeInternalError
			message = err.Error()
		}
	}

	var rawData *json.RawMessage
	if data == nil {
		data = make(map[string]interface{})
	}
	data["stack"] = fmt.Sprintf("%+v", err)
	data["butlerVersion"] = r.ButlerVersionString

	marshalledData, marshalErr := json.Marshal(data)
	if marshalErr == nil {
		rawMessage := json.RawMessage(marshalledData)
		rawData = &rawMessage
	}

	origConn.ReplyWithError(ctx, req.ID, &jsonrpc2.Error{
		Code:    code,
		Message: message,
		Data:    rawData,
	})
}

type RequestContext struct {
	Ctx         context.Context
	Consumer    *state.Consumer
	Params      *json.RawMessage
	Conn        Conn
	CancelFuncs *CancelFuncs
	dbPool      *sqlite.Pool
	Client      GetClientFunc

	ButlerVersion       string
	ButlerVersionString string

	notificationInterceptors map[string]NotificationInterceptor
	tracker                  *progress.Tracker
}

type WithParamsFunc func() (interface{}, error)

type NotificationInterceptor func(method string, params interface{}) error

func (rc *RequestContext) Call(method string, params interface{}, res interface{}) error {
	return rc.Conn.Call(rc.Ctx, method, params, res)
}

func (rc *RequestContext) InterceptNotification(method string, interceptor NotificationInterceptor) {
	if rc.notificationInterceptors == nil {
		rc.notificationInterceptors = make(map[string]NotificationInterceptor)
	}
	rc.notificationInterceptors[method] = interceptor
}

func (rc *RequestContext) StopInterceptingNotification(method string) {
	if rc.notificationInterceptors == nil {
		return
	}
	delete(rc.notificationInterceptors, method)
}

func (rc *RequestContext) Notify(method string, params interface{}) error {
	if rc.notificationInterceptors != nil {
		if ni, ok := rc.notificationInterceptors[method]; ok {
			return ni(method, params)
		}
	}
	return rc.Conn.Notify(rc.Ctx, method, params)
}

func (rc *RequestContext) RootClient() *itchio.Client {
	return rc.Client("<keyless>")
}

func (rc *RequestContext) ProfileClient(profileID int64) (*models.Profile, *itchio.Client) {
	if profileID == 0 {
		panic(errors.New("profileId must be non-zero"))
	}

	conn := rc.GetConn()
	defer rc.PutConn(conn)

	profile := models.ProfileByID(conn, profileID)
	if profile == nil {
		panic(errors.Errorf("Could not find profile %d", profileID))
	}

	if profile.APIKey == "" {
		panic(errors.Errorf("Profile %d lacks API key", profileID))
	}

	return profile, rc.Client(profile.APIKey)
}

func (rc *RequestContext) StartProgress() {
	rc.StartProgressWithTotalBytes(0)
}

func (rc *RequestContext) StartProgressWithTotalBytes(totalBytes int64) {
	rc.StartProgressWithInitialAndTotal(0.0, totalBytes)
}

func (rc *RequestContext) StartProgressWithInitialAndTotal(initialProgress float64, totalBytes int64) {
	if rc.tracker != nil {
		rc.Consumer.Warnf("Asked to start progress but already tracking progress!")
		return
	}

	rc.tracker = progress.NewTracker()
	rc.tracker.SetSilent(true)
	rc.tracker.SetProgress(initialProgress)
	rc.tracker.SetTotalBytes(totalBytes)
	rc.tracker.Start()
}

func (rc *RequestContext) EndProgress() {
	if rc.tracker != nil {
		rc.tracker.Finish()
		rc.tracker = nil
	} else {
		rc.Consumer.Warnf("Asked to stop progress but wasn't tracking progress!")
	}
}

func (rc *RequestContext) GetConn() *sqlite.Conn {
	getCtx, cancel := context.WithTimeout(rc.Ctx, 1*time.Second)
	defer cancel()
	conn := rc.dbPool.Get(getCtx.Done())
	if conn != nil {
		conn.SetInterrupt(rc.Ctx.Done())
		return conn
	}

	panic(errors.WithStack(CodeDatabaseBusy))
	return nil
}

func (rc *RequestContext) PutConn(conn *sqlite.Conn) {
	rc.dbPool.Put(conn)
}

func (rc *RequestContext) WithConn(f func(conn *sqlite.Conn)) {
	conn := rc.GetConn()
	defer rc.PutConn(conn)
	f(conn)
}

func (rc *RequestContext) WithConnBool(f func(conn *sqlite.Conn) bool) bool {
	conn := rc.GetConn()
	defer rc.PutConn(conn)
	return f(conn)
}

type CancelFuncs struct {
	Funcs map[string]context.CancelFunc
}

func (cf *CancelFuncs) Add(id string, f context.CancelFunc) {
	cf.Funcs[id] = f
}

func (cf *CancelFuncs) Remove(id string) {
	delete(cf.Funcs, id)
}

func (cf *CancelFuncs) Call(id string) bool {
	if f, ok := cf.Funcs[id]; ok {
		f()
		delete(cf.Funcs, id)
		return true
	}

	return false
}
<commit_msg>Keep track of origConn and method in RequestContext<commit_after>package butlerd

import (
	"context"
	"encoding/json"
	"fmt"
	"time"

	"github.com/itchio/wharf/werrors"

	"github.com/itchio/httpkit/neterr"
	"github.com/itchio/httpkit/progress"

	"crawshaw.io/sqlite"
	"github.com/itchio/butler/database/models"
	itchio "github.com/itchio/go-itchio"
	"github.com/itchio/wharf/state"
	"github.com/pkg/errors"
	"github.com/sourcegraph/jsonrpc2"
)

type RequestHandler func(rc *RequestContext) (interface{}, error)
type NotificationHandler func(rc *RequestContext)

type GetClientFunc func(key string) *itchio.Client

type Router struct {
	Handlers             map[string]RequestHandler
	NotificationHandlers map[string]NotificationHandler
	CancelFuncs          *CancelFuncs
	dbPool               *sqlite.Pool
	getClient            GetClientFunc

	ButlerVersion       string
	ButlerVersionString string
}

func NewRouter(dbPool *sqlite.Pool, getClient GetClientFunc) *Router {
	return &Router{
		Handlers:             make(map[string]RequestHandler),
		NotificationHandlers: make(map[string]NotificationHandler),
		CancelFuncs: &CancelFuncs{
			Funcs: make(map[string]context.CancelFunc),
		},
		dbPool:    dbPool,
		getClient: getClient,
	}
}

func (r *Router) Register(method string, rh RequestHandler) {
	if _, ok := r.Handlers[method]; ok {
		panic(fmt.Sprintf("Can't register handler twice for %s", method))
	}
	r.Handlers[method] = rh
}

func (r *Router) RegisterNotification(method string, nh NotificationHandler) {
	if _, ok := r.NotificationHandlers[method]; ok {
		panic(fmt.Sprintf("Can't register handler twice for %s", method))
	}
	r.NotificationHandlers[method] = nh
}

func (r *Router) Dispatch(ctx context.Context, origConn *jsonrpc2.Conn, req *jsonrpc2.Request) {
	method := req.Method
	var res interface{}

	conn := &JsonRPC2Conn{origConn}
	consumer, cErr := NewStateConsumer(&NewStateConsumerParams{
		Ctx:  ctx,
		Conn: conn,
	})
	if cErr != nil {
		return
	}

	err := func() (err error) {
		defer func() {
			if r := recover(); r != nil {
				if rErr, ok := r.(error); ok {
					err = errors.WithStack(rErr)
				} else {
					err = errors.Errorf("panic: %v", r)
				}
			}
		}()

		rc := &RequestContext{
			Ctx:         ctx,
			Consumer:    consumer,
			Params:      req.Params,
			Conn:        conn,
			CancelFuncs: r.CancelFuncs,
			dbPool:      r.dbPool,
			Client:      r.getClient,

			ButlerVersion:       r.ButlerVersion,
			ButlerVersionString: r.ButlerVersionString,

			origConn: origConn,
			method:   method,
		}

		if req.Notif {
			if nh, ok := r.NotificationHandlers[req.Method]; ok {
				nh(rc)
			}
		} else {
			if h, ok := r.Handlers[method]; ok {
				rc.Consumer.OnProgress = func(alpha float64) {
					if rc.tracker == nil {
						// skip
						return
					}

					rc.tracker.SetProgress(alpha)
					notif := ProgressNotification{
						Progress: alpha,
						ETA:      rc.tracker.ETA().Seconds(),
						BPS:      rc.tracker.BPS(),
					}
					// cannot use autogenerated wrappers to avoid import cycles
					rc.Notify("Progress", notif)
				}
				rc.Consumer.OnProgressLabel = func(label string) {
					// muffin
				}
				rc.Consumer.OnPauseProgress = func() {
					if rc.tracker != nil {
						rc.tracker.Pause()
					}
				}
				rc.Consumer.OnResumeProgress = func() {
					if rc.tracker != nil {
						rc.tracker.Resume()
					}
				}

				res, err = h(rc)
			} else {
				err = &RpcError{
					Code:    jsonrpc2.CodeMethodNotFound,
					Message: fmt.Sprintf("Method '%s' not found", req.Method),
				}
			}
		}
		return
	}()

	if req.Notif {
		return
	}

	if err == nil {
		err = origConn.Reply(ctx, req.ID, res)
		if err != nil {
			consumer.Errorf("Error while replying: %s", err.Error())
		}
		return
	}

	var code int64
	var message string
	var data map[string]interface{}

	if ee, ok := AsButlerdError(err); ok {
		code = ee.RpcErrorCode()
		message = ee.RpcErrorMessage()
		data = ee.RpcErrorData()
	} else {
		if neterr.IsNetworkError(err) {
			code = int64(CodeNetworkDisconnected)
			message = CodeNetworkDisconnected.Error()
		} else if errors.Cause(err) == werrors.ErrCancelled {
			code = int64(CodeOperationCancelled)
			message = CodeOperationCancelled.Error()
		} else {
			code = jsonrpc2.CodeInternalError
			message = err.Error()
		}
	}

	var rawData *json.RawMessage
	if data == nil {
		data = make(map[string]interface{})
	}
	data["stack"] = fmt.Sprintf("%+v", err)
	data["butlerVersion"] = r.ButlerVersionString

	marshalledData, marshalErr := json.Marshal(data)
	if marshalErr == nil {
		rawMessage := json.RawMessage(marshalledData)
		rawData = &rawMessage
	}

	origConn.ReplyWithError(ctx, req.ID, &jsonrpc2.Error{
		Code:    code,
		Message: message,
		Data:    rawData,
	})
}

type RequestContext struct {
	Ctx         context.Context
	Consumer    *state.Consumer
	Params      *json.RawMessage
	Conn        Conn
	CancelFuncs *CancelFuncs
	dbPool      *sqlite.Pool
	Client      GetClientFunc

	ButlerVersion       string
	ButlerVersionString string

	notificationInterceptors map[string]NotificationInterceptor
	tracker                  *progress.Tracker

	method   string
	origConn *jsonrpc2.Conn
}

type WithParamsFunc func() (interface{}, error)

type NotificationInterceptor func(method string, params interface{}) error

func (rc *RequestContext) Call(method string, params interface{}, res interface{}) error {
	return rc.Conn.Call(rc.Ctx, method, params, res)
}

func (rc *RequestContext) InterceptNotification(method string, interceptor NotificationInterceptor) {
	if rc.notificationInterceptors == nil {
		rc.notificationInterceptors = make(map[string]NotificationInterceptor)
	}
	rc.notificationInterceptors[method] = interceptor
}

func (rc *RequestContext) StopInterceptingNotification(method string) {
	if rc.notificationInterceptors == nil {
		return
	}
	delete(rc.notificationInterceptors, method)
}

func (rc *RequestContext) Notify(method string, params interface{}) error {
	if rc.notificationInterceptors != nil {
		if ni, ok := rc.notificationInterceptors[method]; ok {
			return ni(method, params)
		}
	}
	return rc.Conn.Notify(rc.Ctx, method, params)
}

func (rc *RequestContext) RootClient() *itchio.Client {
	return rc.Client("<keyless>")
}

func (rc *RequestContext) ProfileClient(profileID int64) (*models.Profile, *itchio.Client) {
	if profileID == 0 {
		panic(errors.New("profileId must be non-zero"))
	}

	conn := rc.GetConn()
	defer rc.PutConn(conn)

	profile := models.ProfileByID(conn, profileID)
	if profile == nil {
		panic(errors.Errorf("Could not find profile %d", profileID))
	}

	if profile.APIKey == "" {
		panic(errors.Errorf("Profile %d lacks API key", profileID))
	}

	return profile, rc.Client(profile.APIKey)
}

func (rc *RequestContext) StartProgress() {
	rc.StartProgressWithTotalBytes(0)
}

func (rc *RequestContext) StartProgressWithTotalBytes(totalBytes int64) {
	rc.StartProgressWithInitialAndTotal(0.0, totalBytes)
}

func (rc *RequestContext) StartProgressWithInitialAndTotal(initialProgress float64, totalBytes int64) {
	if rc.tracker != nil {
		rc.Consumer.Warnf("Asked to start progress but already tracking progress!")
		return
	}

	rc.tracker = progress.NewTracker()
	rc.tracker.SetSilent(true)
	rc.tracker.SetProgress(initialProgress)
	rc.tracker.SetTotalBytes(totalBytes)
	rc.tracker.Start()
}

func (rc *RequestContext) EndProgress() {
	if rc.tracker != nil {
		rc.tracker.Finish()
		rc.tracker = nil
	} else {
		rc.Consumer.Warnf("Asked to stop progress but wasn't tracking progress!")
	}
}

func (rc *RequestContext) GetConn() *sqlite.Conn {
	getCtx, cancel := context.WithTimeout(rc.Ctx, 1*time.Second)
	defer cancel()
	conn := rc.dbPool.Get(getCtx.Done())
	if conn != nil {
		conn.SetInterrupt(rc.Ctx.Done())
		return conn
	}

	panic(errors.WithStack(CodeDatabaseBusy))
	return nil
}

func (rc *RequestContext) PutConn(conn *sqlite.Conn) {
	rc.dbPool.Put(conn)
}

func (rc *RequestContext) WithConn(f func(conn *sqlite.Conn)) {
	conn := rc.GetConn()
	defer rc.PutConn(conn)
	f(conn)
}

func (rc *RequestContext) WithConnBool(f func(conn *sqlite.Conn) bool) bool {
	conn := rc.GetConn()
	defer rc.PutConn(conn)
	return f(conn)
}

type CancelFuncs struct {
	Funcs map[string]context.CancelFunc
}

func (cf *CancelFuncs) Add(id string, f context.CancelFunc) {
	cf.Funcs[id] = f
}

func (cf *CancelFuncs) Remove(id string) {
	delete(cf.Funcs, id)
}

func (cf *CancelFuncs) Call(id string) bool {
	if f, ok := cf.Funcs[id]; ok {
		f()
		delete(cf.Funcs, id)
		return true
	}

	return false
}
<|endoftext|>


<commit_before>// Package broker is the micro broker
package broker

import (
	"time"

	"github.com/micro/cli"
	"github.com/micro/go-micro"
	"github.com/micro/go-micro/util/log"
	"github.com/micro/go-micro/broker/service/handler"
	pb "github.com/micro/go-micro/broker/service/proto"
	rcli "github.com/micro/micro/cli"
)

var (
	// Name of the broker
	Name = "go.micro.broker"
	// The address of the broker
	Address = ":8001"
)

func run(ctx *cli.Context, srvOpts ...micro.Option) {
	log.Name("broker")

	if len(ctx.GlobalString("server_name")) > 0 {
		Name = ctx.GlobalString("server_name")
	}
	if len(ctx.String("address")) > 0 {
		Address = ctx.String("address")
	}

	// Init plugins
	for _, p := range Plugins() {
		p.Init(ctx)
	}

	// service opts
	srvOpts = append(srvOpts, micro.Name(Name))
	if i := time.Duration(ctx.GlobalInt("register_ttl")); i > 0 {
		srvOpts = append(srvOpts, micro.RegisterTTL(i*time.Second))
	}
	if i := time.Duration(ctx.GlobalInt("register_interval")); i > 0 {
		srvOpts = append(srvOpts, micro.RegisterInterval(i*time.Second))
	}

	// set address
	if len(Address) > 0 {
		srvOpts = append(srvOpts, micro.Address(Address))
	}

	// new service
	service := micro.NewService(srvOpts...)

	// register the broker handler
	pb.RegisterBrokerHandler(service.Server(), &handler.Broker{
		// using the mdns broker
		Broker: service.Options().Broker,
	})

	// run the service
	service.Run()
}

func Commands(options ...micro.Option) []cli.Command {
	command := cli.Command{
		Name:  "broker",
		Usage: "Run the message broker",
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:   "address",
				Usage:  "Set the broker http address e.g 0.0.0.0:8001",
				EnvVar: "MICRO_SERVER_ADDRESS",
			},
		},
		Action: func(ctx *cli.Context) {
			run(ctx, options...)
		},
		Subcommands: rcli.RegistryCommands(),
	}

	for _, p := range Plugins() {
		if cmds := p.Commands(); len(cmds) > 0 {
			command.Subcommands = append(command.Subcommands, cmds...)
		}

		if flags := p.Flags(); len(flags) > 0 {
			command.Flags = append(command.Flags, flags...)
		}
	}

	return []cli.Command{command}
}
<commit_msg>Go fmt<commit_after>// Package broker is the micro broker
package broker

import (
	"time"

	"github.com/micro/cli"
	"github.com/micro/go-micro"
	"github.com/micro/go-micro/broker/service/handler"
	pb "github.com/micro/go-micro/broker/service/proto"
	"github.com/micro/go-micro/util/log"
	rcli "github.com/micro/micro/cli"
)

var (
	// Name of the broker
	Name = "go.micro.broker"
	// The address of the broker
	Address = ":8001"
)

func run(ctx *cli.Context, srvOpts ...micro.Option) {
	log.Name("broker")

	if len(ctx.GlobalString("server_name")) > 0 {
		Name = ctx.GlobalString("server_name")
	}
	if len(ctx.String("address")) > 0 {
		Address = ctx.String("address")
	}

	// Init plugins
	for _, p := range Plugins() {
		p.Init(ctx)
	}

	// service opts
	srvOpts = append(srvOpts, micro.Name(Name))
	if i := time.Duration(ctx.GlobalInt("register_ttl")); i > 0 {
		srvOpts = append(srvOpts, micro.RegisterTTL(i*time.Second))
	}
	if i := time.Duration(ctx.GlobalInt("register_interval")); i > 0 {
		srvOpts = append(srvOpts, micro.RegisterInterval(i*time.Second))
	}

	// set address
	if len(Address) > 0 {
		srvOpts = append(srvOpts, micro.Address(Address))
	}

	// new service
	service := micro.NewService(srvOpts...)

	// register the broker handler
	pb.RegisterBrokerHandler(service.Server(), &handler.Broker{
		// using the mdns broker
		Broker: service.Options().Broker,
	})

	// run the service
	service.Run()
}

func Commands(options ...micro.Option) []cli.Command {
	command := cli.Command{
		Name:  "broker",
		Usage: "Run the message broker",
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:   "address",
				Usage:  "Set the broker http address e.g 0.0.0.0:8001",
				EnvVar: "MICRO_SERVER_ADDRESS",
			},
		},
		Action: func(ctx *cli.Context) {
			run(ctx, options...)
		},
		Subcommands: rcli.RegistryCommands(),
	}

	for _, p := range Plugins() {
		if cmds := p.Commands(); len(cmds) > 0 {
			command.Subcommands = append(command.Subcommands, cmds...)
		}

		if flags := p.Flags(); len(flags) > 0 {
			command.Flags = append(command.Flags, flags...)
		}
	}

	return []cli.Command{command}
}
<|endoftext|>


<commit_before>package terraform

import (
	"github.com/hashicorp/terraform/dag"
	"github.com/hashicorp/terraform/tfdiags"
)

// NodePlannableResource represents a resource that is "plannable":
// it is ready to be planned in order to create a diff.
type NodePlannableResource struct {
	*NodeAbstractResource
}

var (
	_ GraphNodeSubPath              = (*NodePlannableResource)(nil)
	_ GraphNodeDynamicExpandable    = (*NodePlannableResource)(nil)
	_ GraphNodeReferenceable        = (*NodePlannableResource)(nil)
	_ GraphNodeReferencer           = (*NodePlannableResource)(nil)
	_ GraphNodeResource             = (*NodePlannableResource)(nil)
	_ GraphNodeAttachResourceConfig = (*NodePlannableResource)(nil)
)

// GraphNodeDynamicExpandable
func (n *NodePlannableResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
	var diags tfdiags.Diagnostics

	count, countDiags := evaluateResourceCountExpression(n.Config.Count, ctx)
	diags = diags.Append(countDiags)
	if countDiags.HasErrors() {
		return nil, diags.Err()
	}

	// Next we need to potentially rename an instance address in the state
	// if we're transitioning whether "count" is set at all.
	fixResourceCountSetTransition(ctx, n.ResourceAddr().Resource, count != -1)

	// Grab the state which we read
	state, lock := ctx.State()
	lock.RLock()
	defer lock.RUnlock()

	// The concrete resource factory we'll use
	concreteResource := func(a *NodeAbstractResourceInstance) dag.Vertex {
		// Add the config and state since we don't do that via transforms
		a.Config = n.Config
		a.ResolvedProvider = n.ResolvedProvider
		a.Schema = n.Schema

		return &NodePlannableResourceInstance{
			NodeAbstractResourceInstance: a,
		}
	}

	// The concrete resource factory we'll use for orphans
	concreteResourceOrphan := func(a *NodeAbstractResourceInstance) dag.Vertex {
		// Add the config and state since we don't do that via transforms
		a.Config = n.Config
		a.ResolvedProvider = n.ResolvedProvider
		a.Schema = n.Schema

		return &NodePlannableResourceInstanceOrphan{
			NodeAbstractResourceInstance: a,
		}
	}

	// Start creating the steps
	steps := []GraphTransformer{
		// Expand the count.
		&ResourceCountTransformer{
			Concrete: concreteResource,
			Schema:   n.Schema,
			Count:    count,
			Addr:     n.ResourceAddr(),
		},

		// Add the count orphans
		&OrphanResourceCountTransformer{
			Concrete: concreteResourceOrphan,
			Count:    count,
			Addr:     n.ResourceAddr(),
			State:    state,
		},

		// Attach the state
		&AttachStateTransformer{State: state},

		// Targeting
		&TargetsTransformer{Targets: n.Targets},

		// Connect references so ordering is correct
		&ReferenceTransformer{},

		// Make sure there is a single root
		&RootTransformer{},
	}

	// Build the graph
	b := &BasicGraphBuilder{
		Steps:    steps,
		Validate: true,
		Name:     "NodePlannableResource",
	}
	graph, diags := b.Build(ctx.Path())
	return graph, diags.ErrWithWarnings()
}
<commit_msg>core: attach provisioner schemas in subgraphs<commit_after>package terraform

import (
	"github.com/hashicorp/terraform/dag"
	"github.com/hashicorp/terraform/tfdiags"
)

// NodePlannableResource represents a resource that is "plannable":
// it is ready to be planned in order to create a diff.
type NodePlannableResource struct {
	*NodeAbstractResource
}

var (
	_ GraphNodeSubPath              = (*NodePlannableResource)(nil)
	_ GraphNodeDynamicExpandable    = (*NodePlannableResource)(nil)
	_ GraphNodeReferenceable        = (*NodePlannableResource)(nil)
	_ GraphNodeReferencer           = (*NodePlannableResource)(nil)
	_ GraphNodeResource             = (*NodePlannableResource)(nil)
	_ GraphNodeAttachResourceConfig = (*NodePlannableResource)(nil)
)

// GraphNodeDynamicExpandable
func (n *NodePlannableResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
	var diags tfdiags.Diagnostics

	count, countDiags := evaluateResourceCountExpression(n.Config.Count, ctx)
	diags = diags.Append(countDiags)
	if countDiags.HasErrors() {
		return nil, diags.Err()
	}

	// Next we need to potentially rename an instance address in the state
	// if we're transitioning whether "count" is set at all.
	fixResourceCountSetTransition(ctx, n.ResourceAddr().Resource, count != -1)

	// Grab the state which we read
	state, lock := ctx.State()
	lock.RLock()
	defer lock.RUnlock()

	// The concrete resource factory we'll use
	concreteResource := func(a *NodeAbstractResourceInstance) dag.Vertex {
		// Add the config and state since we don't do that via transforms
		a.Config = n.Config
		a.ResolvedProvider = n.ResolvedProvider
		a.Schema = n.Schema
		a.ProvisionerSchemas = n.ProvisionerSchemas

		return &NodePlannableResourceInstance{
			NodeAbstractResourceInstance: a,
		}
	}

	// The concrete resource factory we'll use for orphans
	concreteResourceOrphan := func(a *NodeAbstractResourceInstance) dag.Vertex {
		// Add the config and state since we don't do that via transforms
		a.Config = n.Config
		a.ResolvedProvider = n.ResolvedProvider
		a.Schema = n.Schema
		a.ProvisionerSchemas = n.ProvisionerSchemas

		return &NodePlannableResourceInstanceOrphan{
			NodeAbstractResourceInstance: a,
		}
	}

	// Start creating the steps
	steps := []GraphTransformer{
		// Expand the count.
		&ResourceCountTransformer{
			Concrete: concreteResource,
			Schema:   n.Schema,
			Count:    count,
			Addr:     n.ResourceAddr(),
		},

		// Add the count orphans
		&OrphanResourceCountTransformer{
			Concrete: concreteResourceOrphan,
			Count:    count,
			Addr:     n.ResourceAddr(),
			State:    state,
		},

		// Attach the state
		&AttachStateTransformer{State: state},

		// Targeting
		&TargetsTransformer{Targets: n.Targets},

		// Connect references so ordering is correct
		&ReferenceTransformer{},

		// Make sure there is a single root
		&RootTransformer{},
	}

	// Build the graph
	b := &BasicGraphBuilder{
		Steps:    steps,
		Validate: true,
		Name:     "NodePlannableResource",
	}
	graph, diags := b.Build(ctx.Path())
	return graph, diags.ErrWithWarnings()
}
<|endoftext|>


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

// +build !windows

package goncurses

// #cgo pkg-config: form
// #include <form.h>
// #include <stdlib.h>
import "C"

import (
	"syscall"
	"unsafe"
)

type Field struct {
	field *C.FIELD
}

type Form struct {
	form *C.FORM
}

func NewField(h, w, tr, lc, oscr, nbuf int) (*Field, error) {
	var new_field Field
	var err error
	new_field.field, err = C.new_field(C.int(h), C.int(w), C.int(tr), C.int(lc),
		C.int(oscr), C.int(nbuf))
	return &new_field, ncursesError(err)
}

// Background returns the field's background character attributes
func (f *Field) Background() Char {
	return Char(C.field_back(f.field))
}

// Buffer returns a string containing the contents of the buffer. The returned
// string will contain whitespace up to the buffer size as set by SetMax or
// the value by the call to NewField
func (f *Field) Buffer() string {
	str := C.field_buffer(f.field, C.int(0))

	return C.GoString(str)
}

// Duplicate the field at the specified coordinates, returning a pointer
// to the newly allocated object.
func (f *Field) Duplicate(y, x int) (*Field, error) {
	var new_field Field
	var err error
	new_field.field, err = C.dup_field(f.field, C.int(y), C.int(x))
	return &new_field, ncursesError(err)
}

// Foreground returns the field's foreground character attributes
func (f *Field) Foreground() Char {
	return Char(C.field_fore(f.field))
}

// Free field's allocated memory. This must be called to prevent memory
// leaks
func (f *Field) Free() error {
	err := C.free_field(f.field)
	f = nil
	return ncursesError(syscall.Errno(err))
}

// Info retrieves the height, width, y, x, offset and buffer size of the
// given field. Pass the memory addess of the variable to store the data
// in or nil.
func (f *Field) Info(h, w, y, x, off, nbuf *int) error {
	err := C.field_info(f.field, (*C.int)(unsafe.Pointer(h)),
		(*C.int)(unsafe.Pointer(w)), (*C.int)(unsafe.Pointer(y)),
		(*C.int)(unsafe.Pointer(x)), (*C.int)(unsafe.Pointer(off)),
		(*C.int)(unsafe.Pointer(nbuf)))
	return ncursesError(syscall.Errno(err))
}

// Just returns the justification type of the field
func (f *Field) Justification() int {
	return int(C.field_just(f.field))
}

// Move the field to the location of the specified coordinates
func (f *Field) Move(y, x int) error {
	err := C.move_field(f.field, C.int(y), C.int(x))
	return ncursesError(syscall.Errno(err))
}

// Options turns features on and off
func (f *Field) Options(opts int, on bool) {
	if on {
		C.field_opts_on(f.field, C.Field_Options(opts))
		return
	}
	C.field_opts_off(f.field, C.Field_Options(opts))
}

// Pad returns the padding character of the field
func (f *Field) Pad() int {
	return int(C.field_pad(f.field))
}

// SetBuffer sets the visible characters in the field. A buffer is empty by
// default.
func (f *Field) SetBuffer(s string) error {
	cstr := C.CString(s)
	defer C.free(unsafe.Pointer(cstr))

	err := C.set_field_buffer(f.field, C.int(0), cstr)
	return ncursesError(syscall.Errno(err))
}

// SetJustification of the field
func (f *Field) SetJustification(just int) error {
	err := C.set_field_just(f.field, C.int(just))
	return ncursesError(syscall.Errno(err))
}

// SetMax sets the maximum size of a field
func (f *Field) SetMax(max int) error {
	err := C.set_max_field(f.field, C.int(max))
	return ncursesError(syscall.Errno(err))
}

// OptionsOff turns feature(s) off
func (f *Field) SetOptionsOff(opts Char) error {
	err := int(C.field_opts_off(f.field, C.Field_Options(opts)))
	if err != C.E_OK {
		return ncursesError(syscall.Errno(err))
	}
	return nil
}

// OptionsOn turns feature(s) on
func (f *Field) SetOptionsOn(opts Char) error {
	err := int(C.field_opts_on(f.field, C.Field_Options(opts)))
	if err != C.E_OK {
		return ncursesError(syscall.Errno(err))
	}
	return nil
}

// SetPad sets the padding character of the field
func (f *Field) SetPad(padch int) error {
	err := C.set_field_pad(f.field, C.int(padch))
	return ncursesError(syscall.Errno(err))
}

// SetBackground character and attributes (colours, etc)
func (f *Field) SetBackground(ch Char) error {
	err := C.set_field_back(f.field, C.chtype(ch))
	return ncursesError(syscall.Errno(err))
}

// SetForeground character and attributes (colours, etc)
func (f *Field) SetForeground(ch Char) error {
	err := C.set_field_fore(f.field, C.chtype(ch))
	return ncursesError(syscall.Errno(err))
}

// NewForm returns a new form object using the fields array supplied as
// an argument
func NewForm(fields []*Field) (Form, error) {
	cfields := make([]*C.FIELD, len(fields)+1)
	for index, field := range fields {
		cfields[index] = field.field
	}
	cfields[len(fields)] = nil

	var form *C.FORM
	var err error
	form, err = C.new_form((**C.FIELD)(&cfields[0]))

	return Form{form}, ncursesError(err)
}

// FieldCount returns the number of fields attached to the Form
func (f *Form) FieldCount() int {
	return int(C.field_count(f.form))
}

// Driver issues the actions requested to the form itself. See the
// corresponding REQ_* constants
func (f *Form) Driver(drvract Key) error {
	err := C.form_driver(f.form, C.int(drvract))
	return ncursesError(syscall.Errno(err))
}

// Free the memory allocated to the form. Forms are not automatically
// free'd by Go's garbage collection system so the memory allocated to
// it must be explicitely free'd
func (f *Form) Free() error {
	err := C.free_form(f.form)
	f = nil
	return ncursesError(syscall.Errno(err))
}

// Post the form, making it visible and interactive
func (f *Form) Post() error {
	err := C.post_form(f.form)
	return ncursesError(syscall.Errno(err))
}

// SetFields overwrites the current fields for the Form with new ones.
// It is important to make sure all prior fields have been freed otherwise
// this action will result in a memory leak
func (f *Form) SetFields(fields []*Field) error {
	cfields := make([]*C.FIELD, len(fields)+1)
	for index, field := range fields {
		cfields[index] = field.field
	}
	cfields[len(fields)] = nil
	err := C.set_form_fields(f.form, (**C.FIELD)(&cfields[0]))
	return ncursesError(syscall.Errno(err))
}

// SetOptions for the form
func (f *Form) SetOptions(opts int) error {
	_, err := C.set_form_opts(f.form, (C.Form_Options)(opts))
	return ncursesError(err)
}

// SetSub sets the subwindow associated with the form
func (f *Form) SetSub(w *Window) error {
	err := int(C.set_form_sub(f.form, w.win))
	return ncursesError(syscall.Errno(err))
}

// SetWindow sets the window associated with the form
func (f *Form) SetWindow(w *Window) error {
	err := int(C.set_form_win(f.form, w.win))
	return ncursesError(syscall.Errno(err))
}

// Sub returns the subwindow assocaiated with the form
func (f *Form) Sub() Window {
	return Window{C.form_sub(f.form)}
}

// UnPost the form, removing it from the interface
func (f *Form) UnPost() error {
	err := C.unpost_form(f.form)
	return ncursesError(syscall.Errno(err))
}
<commit_msg>convert form fields from struct to type<commit_after>// Copyright 2011 Rob Thornton. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build !windows

package goncurses

// #cgo pkg-config: form
// #include <form.h>
// #include <stdlib.h>
import "C"

import (
	"syscall"
	"unsafe"
)

type Field C.FIELD

type Form struct {
	form *C.FORM
}

func NewField(h, w, tr, lc, oscr, nbuf int) (*Field, error) {
	f, err := C.new_field(C.int(h), C.int(w), C.int(tr), C.int(lc),
		C.int(oscr), C.int(nbuf))
	return (*Field)(f), ncursesError(err)
}

// Background returns the field's background character attributes
func (f *Field) Background() Char {
	return Char(C.field_back((*C.FIELD)(f)))
}

// Buffer returns a string containing the contents of the buffer. The returned
// string will contain whitespace up to the buffer size as set by SetMax or
// the value by the call to NewField
func (f *Field) Buffer() string {
	str := C.field_buffer((*C.FIELD)(f), C.int(0))

	return C.GoString(str)
}

// Duplicate the field at the specified coordinates, returning a pointer
// to the newly allocated object.
func (f *Field) Duplicate(y, x int) (*Field, error) {
	nf, err := C.dup_field((*C.FIELD)(f), C.int(y), C.int(x))
	return (*Field)(nf), ncursesError(err)
}

// Foreground returns the field's foreground character attributes
func (f *Field) Foreground() Char {
	return Char(C.field_fore((*C.FIELD)(f)))
}

// Free field's allocated memory. This must be called to prevent memory
// leaks
func (f *Field) Free() error {
	err := C.free_field((*C.FIELD)(f))
	f = nil
	return ncursesError(syscall.Errno(err))
}

// Info retrieves the height, width, y, x, offset and buffer size of the
// given field. Pass the memory addess of the variable to store the data
// in or nil.
func (f *Field) Info(h, w, y, x, off, nbuf *int) error {
	err := C.field_info((*C.FIELD)(f), (*C.int)(unsafe.Pointer(h)),
		(*C.int)(unsafe.Pointer(w)), (*C.int)(unsafe.Pointer(y)),
		(*C.int)(unsafe.Pointer(x)), (*C.int)(unsafe.Pointer(off)),
		(*C.int)(unsafe.Pointer(nbuf)))
	return ncursesError(syscall.Errno(err))
}

// Just returns the justification type of the field
func (f *Field) Justification() int {
	return int(C.field_just((*C.FIELD)(f)))
}

// Move the field to the location of the specified coordinates
func (f *Field) Move(y, x int) error {
	err := C.move_field((*C.FIELD)(f), C.int(y), C.int(x))
	return ncursesError(syscall.Errno(err))
}

// Options turns features on and off
func (f *Field) Options(opts int, on bool) {
	if on {
		C.field_opts_on((*C.FIELD)(f), C.Field_Options(opts))
		return
	}
	C.field_opts_off((*C.FIELD)(f), C.Field_Options(opts))
}

// Pad returns the padding character of the field
func (f *Field) Pad() int {
	return int(C.field_pad((*C.FIELD)(f)))
}

// SetBuffer sets the visible characters in the field. A buffer is empty by
// default.
func (f *Field) SetBuffer(s string) error {
	cstr := C.CString(s)
	defer C.free(unsafe.Pointer(cstr))

	err := C.set_field_buffer((*C.FIELD)(f), C.int(0), cstr)
	return ncursesError(syscall.Errno(err))
}

// SetJustification of the field
func (f *Field) SetJustification(just int) error {
	err := C.set_field_just((*C.FIELD)(f), C.int(just))
	return ncursesError(syscall.Errno(err))
}

// SetMax sets the maximum size of a field
func (f *Field) SetMax(max int) error {
	err := C.set_max_field((*C.FIELD)(f), C.int(max))
	return ncursesError(syscall.Errno(err))
}

// OptionsOff turns feature(s) off
func (f *Field) SetOptionsOff(opts Char) error {
	err := int(C.field_opts_off((*C.FIELD)(f), C.Field_Options(opts)))
	if err != C.E_OK {
		return ncursesError(syscall.Errno(err))
	}
	return nil
}

// OptionsOn turns feature(s) on
func (f *Field) SetOptionsOn(opts Char) error {
	err := int(C.field_opts_on((*C.FIELD)(f), C.Field_Options(opts)))
	if err != C.E_OK {
		return ncursesError(syscall.Errno(err))
	}
	return nil
}

// SetPad sets the padding character of the field
func (f *Field) SetPad(padch int) error {
	err := C.set_field_pad((*C.FIELD)(f), C.int(padch))
	return ncursesError(syscall.Errno(err))
}

// SetBackground character and attributes (colours, etc)
func (f *Field) SetBackground(ch Char) error {
	err := C.set_field_back((*C.FIELD)(f), C.chtype(ch))
	return ncursesError(syscall.Errno(err))
}

// SetForeground character and attributes (colours, etc)
func (f *Field) SetForeground(ch Char) error {
	err := C.set_field_fore((*C.FIELD)(f), C.chtype(ch))
	return ncursesError(syscall.Errno(err))
}

// NewForm returns a new form object using the fields array supplied as
// an argument
func NewForm(fields []*Field) (Form, error) {
	//cfields := make([]*C.FIELD, len(fields)+1)
	//for index, field := range fields {
	//cfields[index] = field.field
	//}
	//cfields[len(fields)] = nil

	/* append nil field? */

	//var form *C.FORM
	//var err error
	form, err := C.new_form((**C.FIELD)(unsafe.Pointer(&fields[0])))

	return Form{form}, ncursesError(err)
}

// FieldCount returns the number of fields attached to the Form
func (f *Form) FieldCount() int {
	return int(C.field_count(f.form))
}

// Driver issues the actions requested to the form itself. See the
// corresponding REQ_* constants
func (f *Form) Driver(drvract Key) error {
	err := C.form_driver(f.form, C.int(drvract))
	return ncursesError(syscall.Errno(err))
}

// Free the memory allocated to the form. Forms are not automatically
// free'd by Go's garbage collection system so the memory allocated to
// it must be explicitely free'd
func (f *Form) Free() error {
	err := C.free_form(f.form)
	f = nil
	return ncursesError(syscall.Errno(err))
}

// Post the form, making it visible and interactive
func (f *Form) Post() error {
	err := C.post_form(f.form)
	return ncursesError(syscall.Errno(err))
}

// SetFields overwrites the current fields for the Form with new ones.
// It is important to make sure all prior fields have been freed otherwise
// this action will result in a memory leak
func (f *Form) SetFields(fields []*Field) error {
	//cfields := make([]*C.FIELD, len(fields)+1)
	//for index, field := range fields {
	//cfields[index] = field.field
	//}
	//cfields[len(fields)] = nil
	err := C.set_form_fields(f.form, (**C.FIELD)(unsafe.Pointer(&fields[0])))
	return ncursesError(syscall.Errno(err))
}

// SetOptions for the form
func (f *Form) SetOptions(opts int) error {
	_, err := C.set_form_opts(f.form, (C.Form_Options)(opts))
	return ncursesError(err)
}

// SetSub sets the subwindow associated with the form
func (f *Form) SetSub(w *Window) error {
	err := int(C.set_form_sub(f.form, w.win))
	return ncursesError(syscall.Errno(err))
}

// SetWindow sets the window associated with the form
func (f *Form) SetWindow(w *Window) error {
	err := int(C.set_form_win(f.form, w.win))
	return ncursesError(syscall.Errno(err))
}

// Sub returns the subwindow assocaiated with the form
func (f *Form) Sub() Window {
	return Window{C.form_sub(f.form)}
}

// UnPost the form, removing it from the interface
func (f *Form) UnPost() error {
	err := C.unpost_form(f.form)
	return ncursesError(syscall.Errno(err))
}
<|endoftext|>