<commit_before>// Copyright 2017 Google Inc.
//
// 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 dns implements a DNS prober. It sends UDP DNS queries to a list of
targets and reports statistics on queries sent, queries received, and latency
experienced.

This prober uses the DNS library in /third_party/golang/dns/dns to construct,
send, and receive DNS messages. Every message is sent on a different UDP port.
Queries to each target are sent in parallel.
*/
package dns

import (
	"context"
	"errors"
	"fmt"
	"net"
	"strings"
	"sync"
	"time"

	"github.com/google/cloudprober/logger"
	"github.com/google/cloudprober/metrics"
	configpb "github.com/google/cloudprober/probes/dns/proto"
	"github.com/google/cloudprober/probes/options"
	"github.com/google/cloudprober/probes/probeutils"
	"github.com/miekg/dns"
)

// Client provides a DNS client interface for required functionality.
// This makes it possible to mock.
type Client interface {
	Exchange(*dns.Msg, string) (*dns.Msg, time.Duration, error)
	SetReadTimeout(time.Duration)
}

// ClientImpl is a concrete DNS client that can be instantiated.
type ClientImpl struct {
	dns.Client
}

// SetReadTimeout allows write-access to the underlying ReadTimeout variable.
func (c *ClientImpl) SetReadTimeout(d time.Duration) {
	c.ReadTimeout = d
}

// Probe holds aggregate information about all probe runs, per-target.
type Probe struct {
	name string
	opts *options.Options
	c    *configpb.ProbeConf
	l    *logger.Logger

	// book-keeping params
	targets []string
	msg     *dns.Msg
	client  Client
}

// probeRunResult captures the results of a single probe run. The way we work with
// stats makes sure that probeRunResult and its fields are not accessed concurrently
// (see documentation with statsKeeper below). That's the reason we use metrics.Int
// types instead of metrics.AtomicInt.
type probeRunResult struct {
	target            string
	total             metrics.Int
	success           metrics.Int
	latency           metrics.Float
	timeouts          metrics.Int
	validationFailure *metrics.Map
}

// Metrics converts probeRunResult into metrics.EventMetrics object
func (prr probeRunResult) Metrics() *metrics.EventMetrics {
	return metrics.NewEventMetrics(time.Now()).
		AddMetric("total", &prr.total).
		AddMetric("success", &prr.success).
		AddMetric("latency", &prr.latency).
		AddMetric("timeouts", &prr.timeouts).
		AddMetric("validation_failure", prr.validationFailure)
}

// Target returns the p.target.
func (prr probeRunResult) Target() string {
	return prr.target
}

// Init initializes the probe with the given params.
func (p *Probe) Init(name string, opts *options.Options) error {
	c, ok := opts.ProbeConf.(*configpb.ProbeConf)
	if !ok {
		return errors.New("no dns config")
	}
	p.c = c
	p.name = name
	p.opts = opts
	if p.l = opts.Logger; p.l == nil {
		p.l = &logger.Logger{}
	}
	p.targets = p.opts.Targets.List()

	// I believe these objects are safe for concurrent use by multiple goroutines
	// (although the documentation doesn't explicitly say so). It uses locks
	// internally and the underlying net.Conn declares that multiple goroutines
	// may invoke methods on a net.Conn simultaneously.
	p.msg = new(dns.Msg)
	queryType := p.c.GetQueryType()
	if queryType == configpb.QueryType_NONE || int32(queryType) >= int32(dns.TypeReserved) {
		return fmt.Errorf("dns_probe(%v): invalid query type %v", name, queryType)
	}
	p.msg.SetQuestion(dns.Fqdn(p.c.GetResolvedDomain()), uint16(queryType))

	p.client = new(ClientImpl)
	// Use ReadTimeout because DialTimeout for UDP is not the RTT.
	p.client.SetReadTimeout(p.opts.Timeout)

	return nil
}

// Return true if the underlying error indicates a dns.Client timeout.
// In our case, we're using the ReadTimeout- time until response is read.
func isClientTimeout(err error) bool {
	e, ok := err.(*net.OpError)
	return ok && e != nil && e.Timeout()
}

// validateResponse checks status code and answer section for correctness and
// returns true if the response is valid. In case of validation failures, it
// also updates the result structure.
func (p *Probe) validateResponse(resp *dns.Msg, target string, result *probeRunResult) bool {
	if resp == nil || resp.Rcode != dns.RcodeSuccess {
		p.l.Warningf("Target(%s): error in response %v", target, resp)
		return false
	}

	// Validate number of answers in response.
	// TODO: Move this logic to validators.
	minAnswers := p.c.GetMinAnswers()
	if minAnswers > 0 && uint32(len(resp.Answer)) < minAnswers {
		p.l.Warningf("Target(%s): too few answers - got %d want %d.\n\tAnswerBlock: %v",
			target, len(resp.Answer), minAnswers, resp.Answer)
		return false
	}

	if p.opts.Validators == nil {
		return true
	}
	failedValidations := []string{}
	answers := []string{}

	for _, rr := range resp.Answer {
		if rr != nil {
			answers = append(answers, rr.String())
		}
	}
	respBytes := []byte(strings.Join(answers, "\n"))
	for name, v := range p.opts.Validators {
		// TODO: pass along "resp" instead of nil when we add a DNS validator.
		success, err := v.Validate(nil, respBytes)
		if err != nil {
			p.l.Errorf("Error while running the validator %s: %v", name, err)
			continue
		}
		if !success {
			result.validationFailure.IncKey(name)
			failedValidations = append(failedValidations, name)
		}
	}
	if len(failedValidations) > 0 {
		p.l.Debugf("Target(%s): validators %v failed. Resp: %v", target, failedValidations, answers)
		return false
	}
	return true
}

func (p *Probe) runProbe(resultsChan chan<- probeutils.ProbeResult) {
	// Refresh the list of targets to probe.
	p.targets = p.opts.Targets.List()

	wg := sync.WaitGroup{}
	for _, target := range p.targets {
		wg.Add(1)

		// Launch a separate goroutine for each target.
		// Write probe results to the "resultsChan" channel.
		go func(target string, resultsChan chan<- probeutils.ProbeResult) {
			defer wg.Done()

			result := probeRunResult{
				target:            target,
				validationFailure: metrics.NewMap("validator", &metrics.Int{}),
			}

			fullTarget := net.JoinHostPort(target, "53")
			result.total.Inc()
			resp, latency, err := p.client.Exchange(p.msg, fullTarget)

			if err != nil {
				if isClientTimeout(err) {
					p.l.Warningf("Target(%s): client.Exchange: Timeout error: %v", fullTarget, err)
					result.timeouts.Inc()
				} else {
					p.l.Warningf("Target(%s): client.Exchange: %v", fullTarget, err)
				}
			} else if p.validateResponse(resp, fullTarget, &result) {
				result.success.Inc()
				result.latency.AddFloat64(latency.Seconds() / p.opts.LatencyUnit.Seconds())
			}
			resultsChan <- result
		}(target, resultsChan)
	}

	// Wait until all probes are done.
	wg.Wait()
}

// Start starts and runs the probe indefinitely.
func (p *Probe) Start(ctx context.Context, dataChan chan *metrics.EventMetrics) {
	resultsChan := make(chan probeutils.ProbeResult, len(p.targets))

	// This function is used by StatsKeeper to get the latest list of targets.
	// TODO(manugarg): Make p.targets mutex protected as it's read and written by concurrent goroutines.
	targetsFunc := func() []string {
		return p.targets
	}
	go probeutils.StatsKeeper(ctx, "dns", p.name, time.Duration(p.c.GetStatsExportIntervalMsec())*time.Millisecond, targetsFunc, resultsChan, dataChan, p.l)

	for range time.Tick(p.opts.Interval) {
		// Don't run another probe if context is canceled already.
		select {
		case <-ctx.Done():
			return
		default:
		}
		p.runProbe(resultsChan)
	}
}
<commit_msg>Add latency distribution support to DNS probe type.<commit_after>// Copyright 2017 Google Inc.
//
// 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 dns implements a DNS prober. It sends UDP DNS queries to a list of
targets and reports statistics on queries sent, queries received, and latency
experienced.

This prober uses the DNS library in /third_party/golang/dns/dns to construct,
send, and receive DNS messages. Every message is sent on a different UDP port.
Queries to each target are sent in parallel.
*/
package dns

import (
	"context"
	"errors"
	"fmt"
	"net"
	"strings"
	"sync"
	"time"

	"github.com/google/cloudprober/logger"
	"github.com/google/cloudprober/metrics"
	configpb "github.com/google/cloudprober/probes/dns/proto"
	"github.com/google/cloudprober/probes/options"
	"github.com/google/cloudprober/probes/probeutils"
	"github.com/miekg/dns"
)

// Client provides a DNS client interface for required functionality.
// This makes it possible to mock.
type Client interface {
	Exchange(*dns.Msg, string) (*dns.Msg, time.Duration, error)
	SetReadTimeout(time.Duration)
}

// ClientImpl is a concrete DNS client that can be instantiated.
type ClientImpl struct {
	dns.Client
}

// SetReadTimeout allows write-access to the underlying ReadTimeout variable.
func (c *ClientImpl) SetReadTimeout(d time.Duration) {
	c.ReadTimeout = d
}

// Probe holds aggregate information about all probe runs, per-target.
type Probe struct {
	name string
	opts *options.Options
	c    *configpb.ProbeConf
	l    *logger.Logger

	// book-keeping params
	targets []string
	msg     *dns.Msg
	client  Client
}

// probeRunResult captures the results of a single probe run. The way we work with
// stats makes sure that probeRunResult and its fields are not accessed concurrently
// (see documentation with statsKeeper below). That's the reason we use metrics.Int
// types instead of metrics.AtomicInt.
type probeRunResult struct {
	target            string
	total             metrics.Int
	success           metrics.Int
	latency           metrics.Value
	timeouts          metrics.Int
	validationFailure *metrics.Map
}

// Metrics converts probeRunResult into metrics.EventMetrics object
func (prr probeRunResult) Metrics() *metrics.EventMetrics {
	return metrics.NewEventMetrics(time.Now()).
		AddMetric("total", &prr.total).
		AddMetric("success", &prr.success).
		AddMetric("latency", prr.latency).
		AddMetric("timeouts", &prr.timeouts).
		AddMetric("validation_failure", prr.validationFailure)
}

// Target returns the p.target.
func (prr probeRunResult) Target() string {
	return prr.target
}

// Init initializes the probe with the given params.
func (p *Probe) Init(name string, opts *options.Options) error {
	c, ok := opts.ProbeConf.(*configpb.ProbeConf)
	if !ok {
		return errors.New("no dns config")
	}
	p.c = c
	p.name = name
	p.opts = opts
	if p.l = opts.Logger; p.l == nil {
		p.l = &logger.Logger{}
	}
	p.targets = p.opts.Targets.List()

	// I believe these objects are safe for concurrent use by multiple goroutines
	// (although the documentation doesn't explicitly say so). It uses locks
	// internally and the underlying net.Conn declares that multiple goroutines
	// may invoke methods on a net.Conn simultaneously.
	p.msg = new(dns.Msg)
	queryType := p.c.GetQueryType()
	if queryType == configpb.QueryType_NONE || int32(queryType) >= int32(dns.TypeReserved) {
		return fmt.Errorf("dns_probe(%v): invalid query type %v", name, queryType)
	}
	p.msg.SetQuestion(dns.Fqdn(p.c.GetResolvedDomain()), uint16(queryType))

	p.client = new(ClientImpl)
	// Use ReadTimeout because DialTimeout for UDP is not the RTT.
	p.client.SetReadTimeout(p.opts.Timeout)

	return nil
}

// Return true if the underlying error indicates a dns.Client timeout.
// In our case, we're using the ReadTimeout- time until response is read.
func isClientTimeout(err error) bool {
	e, ok := err.(*net.OpError)
	return ok && e != nil && e.Timeout()
}

// validateResponse checks status code and answer section for correctness and
// returns true if the response is valid. In case of validation failures, it
// also updates the result structure.
func (p *Probe) validateResponse(resp *dns.Msg, target string, result *probeRunResult) bool {
	if resp == nil || resp.Rcode != dns.RcodeSuccess {
		p.l.Warningf("Target(%s): error in response %v", target, resp)
		return false
	}

	// Validate number of answers in response.
	// TODO: Move this logic to validators.
	minAnswers := p.c.GetMinAnswers()
	if minAnswers > 0 && uint32(len(resp.Answer)) < minAnswers {
		p.l.Warningf("Target(%s): too few answers - got %d want %d.\n\tAnswerBlock: %v",
			target, len(resp.Answer), minAnswers, resp.Answer)
		return false
	}

	if p.opts.Validators == nil {
		return true
	}
	failedValidations := []string{}
	answers := []string{}

	for _, rr := range resp.Answer {
		if rr != nil {
			answers = append(answers, rr.String())
		}
	}
	respBytes := []byte(strings.Join(answers, "\n"))
	for name, v := range p.opts.Validators {
		// TODO: pass along "resp" instead of nil when we add a DNS validator.
		success, err := v.Validate(nil, respBytes)
		if err != nil {
			p.l.Errorf("Error while running the validator %s: %v", name, err)
			continue
		}
		if !success {
			result.validationFailure.IncKey(name)
			failedValidations = append(failedValidations, name)
		}
	}
	if len(failedValidations) > 0 {
		p.l.Debugf("Target(%s): validators %v failed. Resp: %v", target, failedValidations, answers)
		return false
	}
	return true
}

func (p *Probe) runProbe(resultsChan chan<- probeutils.ProbeResult) {
	// Refresh the list of targets to probe.
	p.targets = p.opts.Targets.List()

	wg := sync.WaitGroup{}
	for _, target := range p.targets {
		wg.Add(1)

		// Launch a separate goroutine for each target.
		// Write probe results to the "resultsChan" channel.
		go func(target string, resultsChan chan<- probeutils.ProbeResult) {
			defer wg.Done()

			result := probeRunResult{
				target:            target,
				validationFailure: metrics.NewMap("validator", &metrics.Int{}),
			}

			if p.opts.LatencyDist != nil {
				result.latency = p.opts.LatencyDist.Clone()
			} else {
				result.latency = metrics.NewFloat(0)
			}

			fullTarget := net.JoinHostPort(target, "53")
			result.total.Inc()
			resp, latency, err := p.client.Exchange(p.msg, fullTarget)

			if err != nil {
				if isClientTimeout(err) {
					p.l.Warningf("Target(%s): client.Exchange: Timeout error: %v", fullTarget, err)
					result.timeouts.Inc()
				} else {
					p.l.Warningf("Target(%s): client.Exchange: %v", fullTarget, err)
				}
			} else if p.validateResponse(resp, fullTarget, &result) {
				result.success.Inc()
				result.latency.AddFloat64(latency.Seconds() / p.opts.LatencyUnit.Seconds())
			}
			resultsChan <- result
		}(target, resultsChan)
	}

	// Wait until all probes are done.
	wg.Wait()
}

// Start starts and runs the probe indefinitely.
func (p *Probe) Start(ctx context.Context, dataChan chan *metrics.EventMetrics) {
	resultsChan := make(chan probeutils.ProbeResult, len(p.targets))

	// This function is used by StatsKeeper to get the latest list of targets.
	// TODO(manugarg): Make p.targets mutex protected as it's read and written by concurrent goroutines.
	targetsFunc := func() []string {
		return p.targets
	}
	go probeutils.StatsKeeper(ctx, "dns", p.name, time.Duration(p.c.GetStatsExportIntervalMsec())*time.Millisecond, targetsFunc, resultsChan, dataChan, p.l)

	for range time.Tick(p.opts.Interval) {
		// Don't run another probe if context is canceled already.
		select {
		case <-ctx.Done():
			return
		default:
		}
		p.runProbe(resultsChan)
	}
}
<|endoftext|>


<commit_before>// See https://github.com/ninchat/ninchat-go/blob/master/webhook/example/processor.go for an example.
package webhook

import (
	"encoding/json"
)

type Webhook struct {
	// Kid and Exp are specified only for transports which use signatures
	// (i.e. they are specified for HTTP, but not for AWS Lambda functions).
	Kid string `json:"kid,omitempty"`
	Exp int64  `json:"exp,omitempty"`

	Aud     string    `json:"aud"`
	Event   EventType `json:"event"`
	EventID string    `json:"event_id"`

	// Raw JSON event fields can be used to store the data in lossless form.
	WebhookVerificationJSON    json.RawMessage `json:"webhook_verification,omitempty"`
	AudienceRequestedJSON      json.RawMessage `json:"audience_requested,omitempty"`
	AudienceRequestDroppedJSON json.RawMessage `json:"audience_request_dropped,omitempty"`
	AudienceAcceptedJSON       json.RawMessage `json:"audience_accepted,omitempty"`
	AudienceCompleteJSON       json.RawMessage `json:"audience_complete,omitempty"`
	MessageSentJSON            json.RawMessage `json:"message_sent,omitempty"`
	DataAccessJSON             json.RawMessage `json:"data_access,omitempty"`
}

func (doc *Webhook) WebhookVerificationResponse() (content []byte) {
	content, err := json.Marshal(WebhookVerificationResponse{AudNinchat, doc.WebhookVerificationJSON})
	if err != nil {
		panic(err)
	}
	return
}

func (doc *Webhook) AudienceRequested() (event AudienceRequested, err error) {
	err = json.Unmarshal(doc.AudienceRequestedJSON, &event)
	return
}

func (doc *Webhook) AudienceRequestDropped() (event AudienceRequestDropped, err error) {
	err = json.Unmarshal(doc.AudienceRequestDroppedJSON, &event)
	return
}

func (doc *Webhook) AudienceAccepted() (event AudienceAccepted, err error) {
	err = json.Unmarshal(doc.AudienceAcceptedJSON, &event)
	return
}

func (doc *Webhook) AudienceComplete() (event AudienceComplete, err error) {
	err = json.Unmarshal(doc.AudienceCompleteJSON, &event)
	return
}

func (doc *Webhook) MessageSent() (event AudienceComplete, err error) {
	err = json.Unmarshal(doc.MessageSentJSON, &event)
	return
}

func (doc *Webhook) DataAccess() (event DataAccess, err error) {
	err = json.Unmarshal(doc.DataAccessJSON, &event)
	return
}
<commit_msg>webhook: add WrappedWebhook type<commit_after>// See https://github.com/ninchat/ninchat-go/blob/master/webhook/example/processor.go for an example.
package webhook

import (
	"encoding/json"
)

// WrappedWebhook is the alternative content format.
type WrappedWebhook struct {
	Signature string `json:"signature"`
	JSON      []byte `json:"base64"`
}

// Webhook is the default content format.
type Webhook struct {
	// Kid and Exp are specified only for transports which use signatures
	// (i.e. they are specified for HTTP, but not for AWS Lambda functions).
	Kid string `json:"kid,omitempty"`
	Exp int64  `json:"exp,omitempty"`

	Aud     string    `json:"aud"`
	Event   EventType `json:"event"`
	EventID string    `json:"event_id"`

	// Raw JSON event fields can be used to store the data in lossless form.
	WebhookVerificationJSON    json.RawMessage `json:"webhook_verification,omitempty"`
	AudienceRequestedJSON      json.RawMessage `json:"audience_requested,omitempty"`
	AudienceRequestDroppedJSON json.RawMessage `json:"audience_request_dropped,omitempty"`
	AudienceAcceptedJSON       json.RawMessage `json:"audience_accepted,omitempty"`
	AudienceCompleteJSON       json.RawMessage `json:"audience_complete,omitempty"`
	MessageSentJSON            json.RawMessage `json:"message_sent,omitempty"`
	DataAccessJSON             json.RawMessage `json:"data_access,omitempty"`
}

func (doc *Webhook) WebhookVerificationResponse() (content []byte) {
	content, err := json.Marshal(WebhookVerificationResponse{AudNinchat, doc.WebhookVerificationJSON})
	if err != nil {
		panic(err)
	}
	return
}

func (doc *Webhook) AudienceRequested() (event AudienceRequested, err error) {
	err = json.Unmarshal(doc.AudienceRequestedJSON, &event)
	return
}

func (doc *Webhook) AudienceRequestDropped() (event AudienceRequestDropped, err error) {
	err = json.Unmarshal(doc.AudienceRequestDroppedJSON, &event)
	return
}

func (doc *Webhook) AudienceAccepted() (event AudienceAccepted, err error) {
	err = json.Unmarshal(doc.AudienceAcceptedJSON, &event)
	return
}

func (doc *Webhook) AudienceComplete() (event AudienceComplete, err error) {
	err = json.Unmarshal(doc.AudienceCompleteJSON, &event)
	return
}

func (doc *Webhook) MessageSent() (event AudienceComplete, err error) {
	err = json.Unmarshal(doc.MessageSentJSON, &event)
	return
}

func (doc *Webhook) DataAccess() (event DataAccess, err error) {
	err = json.Unmarshal(doc.DataAccessJSON, &event)
	return
}
<|endoftext|>


<commit_before>package nlis

func findNumberOfLIS(nums []int) int {
	return useDP(nums)
}

func useDPw(nums []int) int {
	n := len(nums)
	if n <= 1 {
		return n
	}
	// dp[i] represents # of lis ended with nums[i]
	// mLen[i] represents the length of LIS ended with nums[i]
	dp, mLen := make([]int, n), make([]int, n)
	for i := range mLen {
		dp[i], mLen[i] = 1, 1
	}
	for i := 1; i < n; i++ {
		for j := i - 1; j >= 0; j-- {
			if nums[i] > nums[j] {
				if mLen[i] < mLen[j]+1 {
					mLen[i] = mLen[j] + 1
					dp[i] = dp[j]
				} else if mLen[i] == mLen[j]+1 {
					dp[i] += dp[j]
				}
			}
		}
	}
	var ret, curMax int
	for i := range mLen {
		if curMax < mLen[i] {
			ret = dp[i]
			curMax = mLen[i]
		} else if curMax == mLen[i] {
			ret += dp[i]
		}
	}
	return ret
}
<commit_msg>fix typo<commit_after>package nlis

func findNumberOfLIS(nums []int) int {
	return useDP(nums)
}

func useDP(nums []int) int {
	n := len(nums)
	if n <= 1 {
		return n
	}
	// dp[i] represents # of lis ended with nums[i]
	// mLen[i] represents the length of LIS ended with nums[i]
	dp, mLen := make([]int, n), make([]int, n)
	for i := range mLen {
		dp[i], mLen[i] = 1, 1
	}
	for i := 1; i < n; i++ {
		for j := i - 1; j >= 0; j-- {
			if nums[i] > nums[j] {
				if mLen[i] < mLen[j]+1 {
					mLen[i] = mLen[j] + 1
					dp[i] = dp[j]
				} else if mLen[i] == mLen[j]+1 {
					dp[i] += dp[j]
				}
			}
		}
	}
	var ret, curMax int
	for i := range mLen {
		if curMax < mLen[i] {
			ret = dp[i]
			curMax = mLen[i]
		} else if curMax == mLen[i] {
			ret += dp[i]
		}
	}
	return ret
}
<|endoftext|>


<commit_before>package actions

import (
	"log"

	"github.com/tcolar/goed/core"
)

// activate the given view, with the cursor at y,x
func (a *ar) EdActivateView(viewId int64, y, x int) {
	d(edActivateView{viewId: viewId, y: y, x: x})
}

// returns the currently active view
func (a *ar) EdCurView() int64 {
	vid := make(chan (int64), 1)
	d(edCurView{viewId: vid})
	return <-vid
}

// delete the given column (by index)
// if 'check' is true it will check if dirty first, in which case it will do nothing
// unless called twice in a row.
func (a *ar) EdDelCol(colIndex int, check bool) {
	d(edDelCol{colIndex: colIndex, check: check})
}

// delete the given view (by id)
// if 'check' is true it will check if dirty first, in which case it will do nothing
// unless called twice in a row.
func (a *ar) EdDelView(viewId int64, check bool) {
	d(edDelView{viewId: viewId, check: check})
}

// Open a file/dir(loc) in the editor
// rel is optionally the path to loc
// viewId is the viewId where to open into (or a new one if viewId<0)
// create indicates whether the file/dir needs to be created if it does not exist.
func (a *ar) EdOpen(loc string, viewId int64, rel string, create bool) int64 {
	vid := make(chan (int64), 1)
	d(edOpen{loc: loc, viewId: viewId, rel: rel, create: create, vid: vid})
	return <-vid
}

// Retuns whether the editor can be quit (ie: are any views "dirty")
func (a *ar) EdQuitCheck() bool {
	answer := make(chan (bool), 1)
	d(edQuitCheck{answer: answer})
	return <-answer
}

// Render/repaint the editor UI
func (a *ar) EdRender() {
	d(edRender{})
}

// resize the editor
func (a *ar) EdResize(h, w int) {
	d(edResize{h: h, w: w})
}

// Show a status message in the satus bar
func (a *ar) EdSetStatus(status string) {
	d(edSetStatus{status: status, err: false})
}

// Show an error message (red) in the status bar.
func (a *ar) EdSetStatusErr(status string) {
	d(edSetStatus{status: status, err: true})
}

// swap two views (their position in the UI)
func (a *ar) EdSwapViews(view1Id, view2Id int64) {
	d(edSwapViews{view1Id: view1Id, view2Id: view2Id})
}

// call flush on the underlying terminal (force sync)
func (a *ar) EdTermFlush() {
	d(edTermFlush{})
}

// returns the viewId of the view that holds a file/dir of the given path.
// or -1 if not found.
func (a *ar) EdViewByLoc(loc string) int64 {
	vid := make(chan (int64), 1)
	d(edViewByLoc{loc: loc, vid: vid})
	return <-vid
}

// move a view to the new coordinates (UI position)
func (a *ar) EdViewMove(viewId int64, y1, x1, y2, x2 int) {
	d(edViewMove{viewId: viewId, y1: y1, x1: x1, y2: y2, x2: x2})
}

// navigate between UI views given the CursorMvmt value (left,right,top,down)
func (a *ar) EdViewNavigate(mvmt core.CursorMvmt) {
	d(edViewNavigate{mvmt})
}

// ########  Impl ......

type edActivateView struct {
	viewId int64
	y, x   int
}

func (a edActivateView) Run() error {
	core.Ed.ViewActivate(a.viewId, a.y, a.x)
	return nil
}

type edCurView struct {
	viewId chan int64
}

func (a edCurView) Run() error {
	a.viewId <- core.Ed.CurViewId()
	return nil
}

type edDelCol struct {
	colIndex int
	check    bool
}

func (a edDelCol) Run() error {
	core.Ed.DelColByIndex(a.colIndex, a.check)
	return nil
}

type edDelView struct {
	viewId int64
	check  bool
}

func (a edDelView) Run() error {
	core.Ed.DelViewByIndex(a.viewId, a.check)
	return nil
}

type edOpen struct {
	loc, rel string
	viewId   int64
	create   bool
	vid      chan int64 // returned new viewid if viewId==-1
}

func (a edOpen) Run() error {
	vid, err := core.Ed.Open(a.loc, a.viewId, a.rel, a.create)
	a.vid <- vid
	if err != nil {
		log.Printf("EdOpen error : %s\n", err.Error())
	}
	return err
}

type edQuitCheck struct {
	answer chan (bool)
}

func (a edQuitCheck) Run() error {
	a.answer <- core.Ed.QuitCheck()
	return nil
}

type edRender struct{}

func (a edRender) Run() error {
	core.Ed.Render()
	return nil
}

type edResize struct {
	h, w int
}

func (a edResize) Run() error {
	core.Ed.Resize(a.h, a.w)
	return nil
}

type edSetStatus struct {
	status string
	err    bool
}

func (a edSetStatus) Run() error {
	if a.err {
		core.Ed.SetStatusErr(a.status)
	} else {
		core.Ed.SetStatus(a.status)
	}
	return nil
}

type edSwapViews struct {
	view1Id, view2Id int64
}

func (a edSwapViews) Run() error {
	core.Ed.SwapViews(a.view1Id, a.view2Id)
	return nil
}

type edTermFlush struct{}

func (a edTermFlush) Run() error {
	core.Ed.TermFlush()
	return nil
}

type edViewByLoc struct {
	loc string
	vid chan int64
}

func (a edViewByLoc) Run() error {
	vid := core.Ed.ViewByLoc(a.loc)
	a.vid <- vid
	return nil
}

type edViewMove struct {
	viewId         int64
	y1, x1, y2, x2 int
}

func (a edViewMove) Run() error {
	v := core.Ed.ViewById(a.viewId)
	if v == nil {
		return nil
	}
	core.Ed.ViewMove(a.y1, a.x1, a.y2, a.x2)
	return nil
}

type edViewNavigate struct {
	mvmt core.CursorMvmt
}

func (a edViewNavigate) Run() error {
	core.Ed.ViewNavigate(a.mvmt)
	return nil
}
<commit_msg>Open terminal action<commit_after>package actions

import (
	"log"

	"github.com/tcolar/goed/core"
)

// activate the given view, with the cursor at y,x
func (a *ar) EdActivateView(viewId int64, y, x int) {
	d(edActivateView{viewId: viewId, y: y, x: x})
}

// returns the currently active view
func (a *ar) EdCurView() int64 {
	vid := make(chan (int64), 1)
	d(edCurView{viewId: vid})
	return <-vid
}

// delete the given column (by index)
// if 'check' is true it will check if dirty first, in which case it will do nothing
// unless called twice in a row.
func (a *ar) EdDelCol(colIndex int, check bool) {
	d(edDelCol{colIndex: colIndex, check: check})
}

// delete the given view (by id)
// if 'check' is true it will check if dirty first, in which case it will do nothing
// unless called twice in a row.
func (a *ar) EdDelView(viewId int64, check bool) {
	d(edDelView{viewId: viewId, check: check})
}

// Open a file/dir(loc) in the editor
// rel is optionally the path to loc
// viewId is the viewId where to open into (or a new one if viewId<0)
// create indicates whether the file/dir needs to be created if it does not exist.
func (a *ar) EdOpen(loc string, viewId int64, rel string, create bool) int64 {
	vid := make(chan (int64), 1)
	d(edOpen{loc: loc, viewId: viewId, rel: rel, create: create, vid: vid})
	return <-vid
}

// Open a new terminal view (~ vt100)
func (a *ar) EdOpenTerm(args []string) int64 {
	vid := make(chan (int64), 1)
	d(edOpenTerm{args: args, vid: vid})
	return <-vid
}

// Retuns whether the editor can be quit (ie: are any views "dirty")
func (a *ar) EdQuitCheck() bool {
	answer := make(chan (bool), 1)
	d(edQuitCheck{answer: answer})
	return <-answer
}

// Render/repaint the editor UI
func (a *ar) EdRender() {
	d(edRender{})
}

// resize the editor
func (a *ar) EdResize(h, w int) {
	d(edResize{h: h, w: w})
}

// Show a status message in the satus bar
func (a *ar) EdSetStatus(status string) {
	d(edSetStatus{status: status, err: false})
}

// Show an error message (red) in the status bar.
func (a *ar) EdSetStatusErr(status string) {
	d(edSetStatus{status: status, err: true})
}

// swap two views (their position in the UI)
func (a *ar) EdSwapViews(view1Id, view2Id int64) {
	d(edSwapViews{view1Id: view1Id, view2Id: view2Id})
}

// call flush on the underlying terminal (force sync)
func (a *ar) EdTermFlush() {
	d(edTermFlush{})
}

// returns the viewId of the view that holds a file/dir of the given path.
// or -1 if not found.
func (a *ar) EdViewByLoc(loc string) int64 {
	vid := make(chan (int64), 1)
	d(edViewByLoc{loc: loc, vid: vid})
	return <-vid
}

// move a view to the new coordinates (UI position)
func (a *ar) EdViewMove(viewId int64, y1, x1, y2, x2 int) {
	d(edViewMove{viewId: viewId, y1: y1, x1: x1, y2: y2, x2: x2})
}

// navigate between UI views given the CursorMvmt value (left,right,top,down)
func (a *ar) EdViewNavigate(mvmt core.CursorMvmt) {
	d(edViewNavigate{mvmt})
}

// ########  Impl ......

type edActivateView struct {
	viewId int64
	y, x   int
}

func (a edActivateView) Run() error {
	core.Ed.ViewActivate(a.viewId, a.y, a.x)
	return nil
}

type edCurView struct {
	viewId chan int64
}

func (a edCurView) Run() error {
	a.viewId <- core.Ed.CurViewId()
	return nil
}

type edDelCol struct {
	colIndex int
	check    bool
}

func (a edDelCol) Run() error {
	core.Ed.DelColByIndex(a.colIndex, a.check)
	return nil
}

type edDelView struct {
	viewId int64
	check  bool
}

func (a edDelView) Run() error {
	core.Ed.DelViewByIndex(a.viewId, a.check)
	return nil
}

type edOpen struct {
	loc, rel string
	viewId   int64
	create   bool
	vid      chan int64 // returned new viewid if viewId==-1
}

func (a edOpen) Run() error {
	vid, err := core.Ed.Open(a.loc, a.viewId, a.rel, a.create)
	a.vid <- vid
	if err != nil {
		log.Printf("EdOpen error : %s\n", err.Error())
	}
	return err
}

type edOpenTerm struct {
	args []string
	vid  chan int64 // returned new viewid if viewId==-1
}

func (a edOpenTerm) Run() error {
	vid := core.Ed.StartTermView(a.args)
	a.vid <- vid
	return nil
}

type edQuitCheck struct {
	answer chan (bool)
}

func (a edQuitCheck) Run() error {
	a.answer <- core.Ed.QuitCheck()
	return nil
}

type edRender struct{}

func (a edRender) Run() error {
	core.Ed.Render()
	return nil
}

type edResize struct {
	h, w int
}

func (a edResize) Run() error {
	core.Ed.Resize(a.h, a.w)
	return nil
}

type edSetStatus struct {
	status string
	err    bool
}

func (a edSetStatus) Run() error {
	if a.err {
		core.Ed.SetStatusErr(a.status)
	} else {
		core.Ed.SetStatus(a.status)
	}
	return nil
}

type edSwapViews struct {
	view1Id, view2Id int64
}

func (a edSwapViews) Run() error {
	core.Ed.SwapViews(a.view1Id, a.view2Id)
	return nil
}

type edTermFlush struct{}

func (a edTermFlush) Run() error {
	core.Ed.TermFlush()
	return nil
}

type edViewByLoc struct {
	loc string
	vid chan int64
}

func (a edViewByLoc) Run() error {
	vid := core.Ed.ViewByLoc(a.loc)
	a.vid <- vid
	return nil
}

type edViewMove struct {
	viewId         int64
	y1, x1, y2, x2 int
}

func (a edViewMove) Run() error {
	v := core.Ed.ViewById(a.viewId)
	if v == nil {
		return nil
	}
	core.Ed.ViewMove(a.y1, a.x1, a.y2, a.x2)
	return nil
}

type edViewNavigate struct {
	mvmt core.CursorMvmt
}

func (a edViewNavigate) Run() error {
	core.Ed.ViewNavigate(a.mvmt)
	return nil
}
<|endoftext|>


<commit_before>/*
   Copyright 2012 the go.wde authors

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

package win

import (
	"image"
	"image/color"
)

var (
	DIBModel color.Model = color.ModelFunc(dibModel)
)

func dibModel(c color.Color) color.Color {
	if _, ok := c.(DIBColor); ok {
		return c
	}
	r, g, b, a := c.RGBA()
	// take alpha channel into account
	if a == 0xffff {
		return DIBColor{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8)}
	}
	if a == 0 {
		return DIBColor{0, 0, 0}
	}
	r = (r * 0xffff) / a
	g = (g * 0xffff) / a
	b = (b * 0xffff) / a
	return DIBColor{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8)}
}

type DIBColor struct {
	R, G, B uint8
}

func (c DIBColor) RGBA() (r, g, b, a uint32) {
	r = uint32(c.R)
	r |= r << 8
	g = uint32(c.G)
	g |= g << 8
	b = uint32(c.B)
	b |= b << 8
	a = 0xffff
	return
}

type DIB struct {
	// Pix holds the image's pixels, in B, G, R order. The pixel at
	// (x, y) starts at Pix[(p.Rect.Max.Y-y-p.Rect.Min.Y-1)*p.Stride + (x-p.Rect.Min.X)*3].
	Pix []uint8
	// Stride is the Pix stride (in bytes) between vertically adjacent pixels.
	Stride int
	// Rect is the image's bounds.
	Rect image.Rectangle
}

func NewDIB(r image.Rectangle) *DIB {
	w, h := r.Dx(), r.Dy()
	// make sure that every scan line is a multiple of 4 bytes
	scanline := (w*3 + 3) & ^0x03
	buf := make([]uint8, scanline*h)
	return &DIB{buf, scanline, r}
}

func (p *DIB) ColorModel() color.Model {
	return DIBModel
}

func (p *DIB) Bounds() image.Rectangle {
	return p.Rect
}

func (p *DIB) At(x, y int) color.Color {
	if !(image.Point{x, y}.In(p.Rect)) {
		return DIBColor{}
	}
	i := p.PixOffset(x, y)
	return DIBColor{p.Pix[i+2], p.Pix[i+1], p.Pix[i+0]}
}

// PixOffset returns the index of the first element of Pix that corresponds to
// the pixel at (x, y).
func (p *DIB) PixOffset(x, y int) int {
	return (p.Rect.Max.Y-y-p.Rect.Min.Y-1)*p.Stride + (x-p.Rect.Min.X)*3
}

func (p *DIB) Set(x, y int, c color.Color) {
	if !(image.Point{x, y}.In(p.Rect)) {
		return
	}
	i := p.PixOffset(x, y)
	c1 := DIBModel.Convert(c).(DIBColor)
	p.Pix[i+0] = c1.B
	p.Pix[i+1] = c1.G
	p.Pix[i+2] = c1.R
}

func (p *DIB) SetDIB(x, y int, c DIBColor) {
	if !(image.Point{x, y}.In(p.Rect)) {
		return
	}
	i := p.PixOffset(x, y)
	p.Pix[i+0] = c.B
	p.Pix[i+1] = c.G
	p.Pix[i+2] = c.R
}

// SubImage returns an image representing the portion of the image p visible
// through r. The returned value shares pixels with the original image.
func (p *DIB) SubImage(r image.Rectangle) image.Image {
	r = r.Intersect(p.Rect)
	// If r1 and r2 are Rectangles, r1.Intersect(r2) is not guaranteed to be inside
	// either r1 or r2 if the intersection is empty. Without explicitly checking for
	// this, the Pix[i:] expression below can panic.
	if r.Empty() {
		return &DIB{}
	}
	i := p.PixOffset(r.Min.X, r.Min.Y)
	return &DIB{
		Pix:    p.Pix[i:],
		Stride: p.Stride,
		Rect:   r,
	}
}

// Opaque scans the entire image and returns whether or not it is fully opaque.
func (p *DIB) Opaque() bool {
	return true
}
<commit_msg>corrected conversion<commit_after>/*
   Copyright 2012 the go.wde authors

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

package win

import (
	"image"
	"image/color"
)

var (
	DIBModel color.Model = color.ModelFunc(dibModel)
)

func dibModel(c color.Color) color.Color {
	if _, ok := c.(DIBColor); ok {
		return c
	}
	r, g, b, a := c.RGBA()
	// take alpha channel into account
	if a == 0xffff {
		return DIBColor{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8)}
	}
	if a == 0 {
		return DIBColor{0, 0, 0}
	}
	r = (r / 0xffff) * a
	g = (g / 0xffff) * a
	b = (b / 0xffff) * a
	return DIBColor{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8)}
}

type DIBColor struct {
	R, G, B uint8
}

func (c DIBColor) RGBA() (r, g, b, a uint32) {
	r = uint32(c.R)
	r |= r << 8
	g = uint32(c.G)
	g |= g << 8
	b = uint32(c.B)
	b |= b << 8
	a = 0xffff
	return
}

type DIB struct {
	// Pix holds the image's pixels, in B, G, R order. The pixel at
	// (x, y) starts at Pix[(p.Rect.Max.Y-y-p.Rect.Min.Y-1)*p.Stride + (x-p.Rect.Min.X)*3].
	Pix []uint8
	// Stride is the Pix stride (in bytes) between vertically adjacent pixels.
	Stride int
	// Rect is the image's bounds.
	Rect image.Rectangle
}

func NewDIB(r image.Rectangle) *DIB {
	w, h := r.Dx(), r.Dy()
	// make sure that every scan line is a multiple of 4 bytes
	scanline := (w*3 + 3) & ^0x03
	buf := make([]uint8, scanline*h)
	return &DIB{buf, scanline, r}
}

func (p *DIB) ColorModel() color.Model {
	return DIBModel
}

func (p *DIB) Bounds() image.Rectangle {
	return p.Rect
}

func (p *DIB) At(x, y int) color.Color {
	if !(image.Point{x, y}.In(p.Rect)) {
		return DIBColor{}
	}
	i := p.PixOffset(x, y)
	return DIBColor{p.Pix[i+2], p.Pix[i+1], p.Pix[i+0]}
}

// PixOffset returns the index of the first element of Pix that corresponds to
// the pixel at (x, y).
func (p *DIB) PixOffset(x, y int) int {
	return (p.Rect.Max.Y-y-p.Rect.Min.Y-1)*p.Stride + (x-p.Rect.Min.X)*3
}

func (p *DIB) Set(x, y int, c color.Color) {
	if !(image.Point{x, y}.In(p.Rect)) {
		return
	}
	i := p.PixOffset(x, y)
	c1 := DIBModel.Convert(c).(DIBColor)
	p.Pix[i+0] = c1.B
	p.Pix[i+1] = c1.G
	p.Pix[i+2] = c1.R
}

func (p *DIB) SetDIB(x, y int, c DIBColor) {
	if !(image.Point{x, y}.In(p.Rect)) {
		return
	}
	i := p.PixOffset(x, y)
	p.Pix[i+0] = c.B
	p.Pix[i+1] = c.G
	p.Pix[i+2] = c.R
}

// SubImage returns an image representing the portion of the image p visible
// through r. The returned value shares pixels with the original image.
func (p *DIB) SubImage(r image.Rectangle) image.Image {
	r = r.Intersect(p.Rect)
	// If r1 and r2 are Rectangles, r1.Intersect(r2) is not guaranteed to be inside
	// either r1 or r2 if the intersection is empty. Without explicitly checking for
	// this, the Pix[i:] expression below can panic.
	if r.Empty() {
		return &DIB{}
	}
	i := p.PixOffset(r.Min.X, r.Min.Y)
	return &DIB{
		Pix:    p.Pix[i:],
		Stride: p.Stride,
		Rect:   r,
	}
}

// Opaque scans the entire image and returns whether or not it is fully opaque.
func (p *DIB) Opaque() bool {
	return true
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"os"
	"time"

	"github.com/guregu/kami"
	"golang.org/x/net/context"
)

var rnd = rand.New(rand.NewSource(time.Now().UnixNano()))

const (
	cardsKey = iota
)

// Card describes a dominion card
type Card struct {
	CostPotions   int    `json:"cost_potions"`
	CostTreasure  int    `json:"cost_treasure"`
	Description   string `json:"description"`
	Event         bool   `json:"event"`
	Expansion     string `json:"expansion"`
	ID            int    `json:"id"`
	IsAttack      bool   `json:"is_attack"`
	IsReaction    bool   `json:"is_reaction"`
	Name          string `json:"name"`
	PlusActions   int    `json:"plus_actions"`
	PlusBuys      int    `json:"plus_buys"`
	PlusCards     int    `json:"plus_cards"`
	PlusTreasure  int    `json:"plus_treasure"`
	Trashes       int    `json:"trashes"`
	Treasure      int    `json:"treasure"`
	VictoryPoints int    `json:"victory_points"`
}

type deckResponse struct {
	Cards                []Card `json:"cards"`
	ColoniesAndPlatinums bool   `json:"colonies_and_platinums"`
	Shelters             bool   `json:"shelters"`
	Potions              bool   `json:"potions"`
	Spoils               bool   `json:"spoils"`
	Ruins                bool   `json:"ruins"`
	Events               []Card `json:"events"`
}

func makeDeck(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	cards, _ := ctx.Value(cardsKey).(map[string]Card)

	var (
		deck            = make([]Card, 0, 10)
		deckSize        = 10
		adventureCards  = 0
		darkAgesCards   = 0
		prosperityCards = 0
		potions         = false
		ruins           = false
		events          = make([]Card, 0, 0)
	)

	for _, card := range cards {
		if card.Expansion == "Dark Ages" {
			darkAgesCards++
		}
		if card.Expansion == "Prosperity" {
			prosperityCards++
		}
		if card.Expansion == "Adventure" {
			adventureCards++
		}
		if card.CostPotions > 0 {
			potions = true
		}

		if card.Event {
			if len(events) < 2 {
				events = append(events, card)
			}
		} else {
			deck = append(deck, card)
		}

		if len(deck) == deckSize {
			break
		}
	}

	resp := deckResponse{
		Cards:                deck,
		ColoniesAndPlatinums: rnd.Intn(deckSize) < prosperityCards,
		Shelters:             rnd.Intn(deckSize) < darkAgesCards,
		Potions:              potions,
		Ruins:                ruins,
		Events:               events,
	}

	enc := json.NewEncoder(w)
	_ = enc.Encode(resp)

	w.Header().Set("Content-Type", "application/json")
}

func main() {
	file, e := ioutil.ReadFile("./data/cards.json")
	if e != nil {
		fmt.Printf("File error: %v\n", e)
		os.Exit(1)
	}

	var cards map[string]Card
	err := json.Unmarshal(file, &cards)
	if err != nil {
		fmt.Printf("JSON Decode error: %v\n", err)
		os.Exit(1)
	}

	ctx := context.Background()
	ctx = context.WithValue(ctx, cardsKey, cards)

	kami.Context = ctx
	kami.Post("/deck", makeDeck)
	kami.Serve()
}
<commit_msg>reorg<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"

	"github.com/guregu/kami"
	"golang.org/x/net/context"

	"github.com/dom-bot/itchy-guacamole/api/card"
	"github.com/dom-bot/itchy-guacamole/api/handlers"
)

func main() {
	file, e := ioutil.ReadFile("./data/cards.json")
	if e != nil {
		fmt.Printf("File error: %v\n", e)
		os.Exit(1)
	}

	var cards map[string]card.Card
	err := json.Unmarshal(file, &cards)
	if err != nil {
		fmt.Printf("JSON Decode error: %v\n", err)
		os.Exit(1)
	}

	ctx := context.Background()
	ctx = context.WithValue(ctx, card.Key, cards)

	kami.Context = ctx
	handlers.SetRoutes()
	kami.Serve()
}
<|endoftext|>


<commit_before>package aqua

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/carbocation/interpose"
	"github.com/gorilla/mux"
	"github.com/tolexo/aero/cache"
	monit "github.com/tolexo/aero/monit"
	"github.com/tolexo/aero/panik"
	"net/http"
	"reflect"
	"strings"
	"time"
)

type endPoint struct {
	caller     MethodInvoker
	info       Fixture
	httpMethod string

	isStdHttpHandler bool
	needsJarInput    bool

	muxUrl    string
	muxVars   []string
	modules   []func(http.Handler) http.Handler
	stash     cache.Cacher
	serviceId string
}

func NewEndPoint(inv MethodInvoker, f Fixture, matchUrl string, httpMethod string, mods map[string]func(http.Handler) http.Handler,
	caches map[string]cache.Cacher, serviceId string) endPoint {

	out := endPoint{
		caller:           inv,
		info:             f,
		isStdHttpHandler: false,
		needsJarInput:    false,
		muxUrl:           matchUrl,
		muxVars:          extractRouteVars(matchUrl),
		httpMethod:       httpMethod,
		modules:          make([]func(http.Handler) http.Handler, 0),
		stash:            nil,
		serviceId:        serviceId,
	}

	if f.Stub == "" {
		out.isStdHttpHandler = out.signatureMatchesDefaultHttpHandler()
		out.needsJarInput = out.needsVariableJar()

		out.validateMuxVarsMatchFuncInputs()
		out.validateFuncInputsAreOfRightType()
		out.validateFuncOutputsAreCorrect()
	}

	// Tag modules used by this endpoint
	if mods != nil && f.Modules != "" {
		names := strings.Split(f.Modules, ",")
		out.modules = make([]func(http.Handler) http.Handler, 0)
		for _, name := range names {
			name = strings.TrimSpace(name)
			fn, found := mods[name]
			if !found {
				panic(fmt.Sprintf("Module:%s not found", name))
			}
			out.modules = append(out.modules, fn)
		}
	}

	// Tag the cache
	if c, ok := caches[f.Cache]; ok {
		out.stash = c
	} else if f.Cache != "" {
		panic("Cache not found: " + f.Cache + " for " + matchUrl)
	}

	return out
}

func (me *endPoint) signatureMatchesDefaultHttpHandler() bool {
	return me.caller.outCount == 0 &&
		me.caller.inpCount == 2 &&
		me.caller.inpParams[0] == "i:net/http.ResponseWriter" &&
		me.caller.inpParams[1] == "*st:net/http.Request"
}

func (me *endPoint) needsVariableJar() bool {
	// needs jar input as the last parameter
	for i := 0; i < len(me.caller.inpParams)-1; i++ {
		if me.caller.inpParams[i] == "st:github.com/tolexo/aqua.Jar" {
			panic("Jar parameter should be the last one: " + me.caller.name)
		}
	}
	return me.caller.inpCount > 0 && me.caller.inpParams[me.caller.inpCount-1] == "st:github.com/tolexo/aqua.Jar"
}

func (me *endPoint) validateMuxVarsMatchFuncInputs() {
	// for non-standard http handlers, the mux vars count should match
	// the count of inputs to the user's method
	if !me.isStdHttpHandler {
		inputs := me.caller.inpCount
		if me.needsJarInput {
			inputs += -1
		}
		if len(me.muxVars) != inputs {
			panic(fmt.Sprintf("%s has %d inputs, but the func (%s) has %d",
				me.muxUrl, len(me.muxVars), me.caller.name, inputs))
		}
	}
}

func (me *endPoint) validateFuncInputsAreOfRightType() {
	if !me.isStdHttpHandler {
		for _, s := range me.caller.inpParams {
			switch s {
			case "st:github.com/tolexo/aqua.Jar":
			case "int":
			case "string":
			default:
				panic("Func input params should be 'int' or 'string'. Observed: " + s + " in: " + me.caller.name)
			}
		}
	}
}

func (me *endPoint) validateFuncOutputsAreCorrect() {

	var accepts = make(map[string]bool)
	accepts["string"] = true
	accepts["map"] = true
	accepts["st:github.com/tolexo/aqua.Sac"] = true
	accepts["*st:github.com/tolexo/aqua.Sac"] = true

	if !me.isStdHttpHandler {
		switch me.caller.outCount {
		case 1:
			_, found := accepts[me.caller.outParams[0]]
			if !found && !strings.HasPrefix(me.caller.outParams[0], "st:") {
				fmt.Println(me.caller.outParams[0])
				panic("Incorrect return type found in: " + me.caller.name)
			}
		case 2:
			if me.caller.outParams[0] != "int" {
				panic("When a func returns two params, the first must be an int (http status code) : " + me.caller.name)
			}
			_, found := accepts[me.caller.outParams[1]]
			if !found && !strings.HasPrefix(me.caller.outParams[1], "st:") {
				panic("Incorrect return type for second return param found in: " + me.caller.name)
			}
		default:
			panic("Incorrect number of returns for Func: " + me.caller.name)
		}
	}
}

// func middleman(next http.Handler) http.Handler {
// 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 		fmt.Println("In the middle >>>>")
// 		next.ServeHTTP(w, r)
// 		fmt.Println("And leaving middle <<<<")
// 	})
// }

func (me *endPoint) setupMuxHandlers(mux *mux.Router) {

	fn := handleIncoming(me)

	m := interpose.New()
	for i, _ := range me.modules {
		m.Use(me.modules[i])
		//fmt.Println("using module:", me.modules[i], reflect.TypeOf(me.modules[i]))
	}
	m.UseHandler(http.HandlerFunc(fn))

	if me.info.Version == "*" {
		mux.Handle(me.muxUrl, m).Methods(me.httpMethod)
	} else {
		urlWithVersion := cleanUrl(me.info.Prefix, "v"+me.info.Version, me.muxUrl)
		urlWithoutVersion := cleanUrl(me.info.Prefix, me.muxUrl)

		// versioned url
		mux.Handle(urlWithVersion, m).Methods(me.httpMethod)

		// content type (style1)
		header1 := fmt.Sprintf("application/%s-v%s+json", me.info.Vendor, me.info.Version)
		mux.Handle(urlWithoutVersion, m).Methods(me.httpMethod).Headers("Accept", header1)

		// content type (style2)
		header2 := fmt.Sprintf("application/%s+json;version=%s", me.info.Vendor, me.info.Version)
		mux.Handle(urlWithoutVersion, m).Methods(me.httpMethod).Headers("Accept", header2)
	}
}

func handleIncoming(e *endPoint) func(http.ResponseWriter, *http.Request) {

	// return stub
	if e.info.Stub != "" {
		return func(w http.ResponseWriter, r *http.Request) {
			d, err := getContent(e.info.Stub)
			if err == nil {
				fmt.Fprintf(w, "%s", d)
			} else {
				w.WriteHeader(400)
				fmt.Fprintf(w, "{ message: \"%s\"}", "Stub path not found")
			}
		}
	}

	return func(w http.ResponseWriter, r *http.Request) {

		cacheHit := false

		// TODO: create less local variables
		// TODO: move vars to closure level

		var out []reflect.Value

		defer func(reqStartTime time.Time) {
			go func() {
				if e.serviceId != "" {
					dur := time.Since(reqStartTime).Seconds() * 1000
					var responseCode int64
					if out != nil && len(out) == 2 && e.caller.outParams[0] == "int" {
						responseCode = out[0].Int()
					}
					monitorParams := monit.MonitorParams{
						ServiceId:    e.serviceId,
						RespTime:     dur,
						ResponseCode: responseCode,
						CacheHit:     cacheHit,
					}
					monit.MonitorMe(monitorParams)

				}
			}()
		}(time.Now())

		var useCache bool = false
		var ttl time.Duration = 0 * time.Second
		var val []byte
		var err error

		if e.info.Ttl != "" {
			ttl, err = time.ParseDuration(e.info.Ttl)
			panik.On(err)
		}
		useCache = r.Method == "GET" && ttl > 0 && e.stash != nil

		muxVals := mux.Vars(r)
		params := make([]string, len(e.muxVars))
		for i, v := range e.muxVars {
			params[i] = muxVals[v]
		}

		if e.isStdHttpHandler {
			//TODO: caching of standard handler
			e.caller.Do([]reflect.Value{reflect.ValueOf(w), reflect.ValueOf(r)})
		} else {
			ref := convertToType(params, e.caller.inpParams)
			if e.needsJarInput {
				ref = append(ref, reflect.ValueOf(NewJar(r)))
			}

			if useCache {
				val, err = e.stash.Get(r.RequestURI)
				if err == nil {
					cacheHit = true
					// fmt.Print(".")
					out = decomposeCachedValues(val, e.caller.outParams)
				} else {
					out = e.caller.Do(ref)
					if len(out) == 2 && e.caller.outParams[0] == "int" {
						code := out[0].Int()
						if code < 200 || code > 299 {
							useCache = false
						}
					}
					if useCache {
						bytes := prepareForCaching(out, e.caller.outParams)
						e.stash.Set(r.RequestURI, bytes, ttl)
						// fmt.Print(":", len(bytes), r.RequestURI)
					}

				}
			} else {
				out = e.caller.Do(ref)
				// fmt.Print("!")
			}
			writeOutput(w, e.caller.outParams, out, e.info.Pretty)
		}
	}
}

func prepareForCaching(r []reflect.Value, outputParams []string) []byte {

	var err error
	buf := new(bytes.Buffer)
	encd := json.NewEncoder(buf)

	for i, _ := range r {
		switch outputParams[i] {
		case "int":
			err = encd.Encode(r[i].Int())
			panik.On(err)
		case "map":
			err = encd.Encode(r[i].Interface().(map[string]interface{}))
			panik.On(err)
		case "string":
			err = encd.Encode(r[i].String())
			panik.On(err)
		case "*st:github.com/tolexo/aqua.Sac":
			err = encd.Encode(r[i].Elem().Interface().(Sac).Data)
		default:
			panic("Unknown type of output to be sent to endpoint cache: " + outputParams[i])
		}
	}

	return buf.Bytes()
}

func decomposeCachedValues(data []byte, outputParams []string) []reflect.Value {

	var err error
	buf := bytes.NewBuffer(data)
	decd := json.NewDecoder(buf)
	out := make([]reflect.Value, len(outputParams))

	for i, o := range outputParams {
		switch o {
		case "int":
			var j int
			err = decd.Decode(&j)
			panik.On(err)
			out[i] = reflect.ValueOf(j)
		case "map":
			var m map[string]interface{}
			err = decd.Decode(&m)
			panik.On(err)
			out[i] = reflect.ValueOf(m)
		case "string":
			var s string
			err = decd.Decode(&s)
			panik.On(err)
			out[i] = reflect.ValueOf(s)
		case "*st:github.com/tolexo/aqua.Sac":
			var m map[string]interface{}
			err = decd.Decode(&m)
			panik.On(err)
			s := NewSac()
			s.Data = m
			out[i] = reflect.ValueOf(s)
		default:
			panic("Unknown type of output to be decoded from endpoint cache:" + o)
		}
	}

	return out

}
<commit_msg>TEC-134 extra line removed<commit_after>package aqua

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/carbocation/interpose"
	"github.com/gorilla/mux"
	"github.com/tolexo/aero/cache"
	monit "github.com/tolexo/aero/monit"
	"github.com/tolexo/aero/panik"
	"net/http"
	"reflect"
	"strings"
	"time"
)

type endPoint struct {
	caller     MethodInvoker
	info       Fixture
	httpMethod string

	isStdHttpHandler bool
	needsJarInput    bool

	muxUrl    string
	muxVars   []string
	modules   []func(http.Handler) http.Handler
	stash     cache.Cacher
	serviceId string
}

func NewEndPoint(inv MethodInvoker, f Fixture, matchUrl string, httpMethod string, mods map[string]func(http.Handler) http.Handler,
	caches map[string]cache.Cacher, serviceId string) endPoint {

	out := endPoint{
		caller:           inv,
		info:             f,
		isStdHttpHandler: false,
		needsJarInput:    false,
		muxUrl:           matchUrl,
		muxVars:          extractRouteVars(matchUrl),
		httpMethod:       httpMethod,
		modules:          make([]func(http.Handler) http.Handler, 0),
		stash:            nil,
		serviceId:        serviceId,
	}

	if f.Stub == "" {
		out.isStdHttpHandler = out.signatureMatchesDefaultHttpHandler()
		out.needsJarInput = out.needsVariableJar()

		out.validateMuxVarsMatchFuncInputs()
		out.validateFuncInputsAreOfRightType()
		out.validateFuncOutputsAreCorrect()
	}

	// Tag modules used by this endpoint
	if mods != nil && f.Modules != "" {
		names := strings.Split(f.Modules, ",")
		out.modules = make([]func(http.Handler) http.Handler, 0)
		for _, name := range names {
			name = strings.TrimSpace(name)
			fn, found := mods[name]
			if !found {
				panic(fmt.Sprintf("Module:%s not found", name))
			}
			out.modules = append(out.modules, fn)
		}
	}

	// Tag the cache
	if c, ok := caches[f.Cache]; ok {
		out.stash = c
	} else if f.Cache != "" {
		panic("Cache not found: " + f.Cache + " for " + matchUrl)
	}

	return out
}

func (me *endPoint) signatureMatchesDefaultHttpHandler() bool {
	return me.caller.outCount == 0 &&
		me.caller.inpCount == 2 &&
		me.caller.inpParams[0] == "i:net/http.ResponseWriter" &&
		me.caller.inpParams[1] == "*st:net/http.Request"
}

func (me *endPoint) needsVariableJar() bool {
	// needs jar input as the last parameter
	for i := 0; i < len(me.caller.inpParams)-1; i++ {
		if me.caller.inpParams[i] == "st:github.com/tolexo/aqua.Jar" {
			panic("Jar parameter should be the last one: " + me.caller.name)
		}
	}
	return me.caller.inpCount > 0 && me.caller.inpParams[me.caller.inpCount-1] == "st:github.com/tolexo/aqua.Jar"
}

func (me *endPoint) validateMuxVarsMatchFuncInputs() {
	// for non-standard http handlers, the mux vars count should match
	// the count of inputs to the user's method
	if !me.isStdHttpHandler {
		inputs := me.caller.inpCount
		if me.needsJarInput {
			inputs += -1
		}
		if len(me.muxVars) != inputs {
			panic(fmt.Sprintf("%s has %d inputs, but the func (%s) has %d",
				me.muxUrl, len(me.muxVars), me.caller.name, inputs))
		}
	}
}

func (me *endPoint) validateFuncInputsAreOfRightType() {
	if !me.isStdHttpHandler {
		for _, s := range me.caller.inpParams {
			switch s {
			case "st:github.com/tolexo/aqua.Jar":
			case "int":
			case "string":
			default:
				panic("Func input params should be 'int' or 'string'. Observed: " + s + " in: " + me.caller.name)
			}
		}
	}
}

func (me *endPoint) validateFuncOutputsAreCorrect() {

	var accepts = make(map[string]bool)
	accepts["string"] = true
	accepts["map"] = true
	accepts["st:github.com/tolexo/aqua.Sac"] = true
	accepts["*st:github.com/tolexo/aqua.Sac"] = true

	if !me.isStdHttpHandler {
		switch me.caller.outCount {
		case 1:
			_, found := accepts[me.caller.outParams[0]]
			if !found && !strings.HasPrefix(me.caller.outParams[0], "st:") {
				fmt.Println(me.caller.outParams[0])
				panic("Incorrect return type found in: " + me.caller.name)
			}
		case 2:
			if me.caller.outParams[0] != "int" {
				panic("When a func returns two params, the first must be an int (http status code) : " + me.caller.name)
			}
			_, found := accepts[me.caller.outParams[1]]
			if !found && !strings.HasPrefix(me.caller.outParams[1], "st:") {
				panic("Incorrect return type for second return param found in: " + me.caller.name)
			}
		default:
			panic("Incorrect number of returns for Func: " + me.caller.name)
		}
	}
}

// func middleman(next http.Handler) http.Handler {
// 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 		fmt.Println("In the middle >>>>")
// 		next.ServeHTTP(w, r)
// 		fmt.Println("And leaving middle <<<<")
// 	})
// }

func (me *endPoint) setupMuxHandlers(mux *mux.Router) {

	fn := handleIncoming(me)

	m := interpose.New()
	for i, _ := range me.modules {
		m.Use(me.modules[i])
		//fmt.Println("using module:", me.modules[i], reflect.TypeOf(me.modules[i]))
	}
	m.UseHandler(http.HandlerFunc(fn))

	if me.info.Version == "*" {
		mux.Handle(me.muxUrl, m).Methods(me.httpMethod)
	} else {
		urlWithVersion := cleanUrl(me.info.Prefix, "v"+me.info.Version, me.muxUrl)
		urlWithoutVersion := cleanUrl(me.info.Prefix, me.muxUrl)

		// versioned url
		mux.Handle(urlWithVersion, m).Methods(me.httpMethod)

		// content type (style1)
		header1 := fmt.Sprintf("application/%s-v%s+json", me.info.Vendor, me.info.Version)
		mux.Handle(urlWithoutVersion, m).Methods(me.httpMethod).Headers("Accept", header1)

		// content type (style2)
		header2 := fmt.Sprintf("application/%s+json;version=%s", me.info.Vendor, me.info.Version)
		mux.Handle(urlWithoutVersion, m).Methods(me.httpMethod).Headers("Accept", header2)
	}
}

func handleIncoming(e *endPoint) func(http.ResponseWriter, *http.Request) {

	// return stub
	if e.info.Stub != "" {
		return func(w http.ResponseWriter, r *http.Request) {
			d, err := getContent(e.info.Stub)
			if err == nil {
				fmt.Fprintf(w, "%s", d)
			} else {
				w.WriteHeader(400)
				fmt.Fprintf(w, "{ message: \"%s\"}", "Stub path not found")
			}
		}
	}

	return func(w http.ResponseWriter, r *http.Request) {

		cacheHit := false

		// TODO: create less local variables
		// TODO: move vars to closure level

		var out []reflect.Value

		defer func(reqStartTime time.Time) {
			go func() {
				if e.serviceId != "" {
					dur := time.Since(reqStartTime).Seconds() * 1000
					var responseCode int64
					if out != nil && len(out) == 2 && e.caller.outParams[0] == "int" {
						responseCode = out[0].Int()
					}
					monitorParams := monit.MonitorParams{
						ServiceId:    e.serviceId,
						RespTime:     dur,
						ResponseCode: responseCode,
						CacheHit:     cacheHit,
					}
					monit.MonitorMe(monitorParams)
				}
			}()
		}(time.Now())

		var useCache bool = false
		var ttl time.Duration = 0 * time.Second
		var val []byte
		var err error

		if e.info.Ttl != "" {
			ttl, err = time.ParseDuration(e.info.Ttl)
			panik.On(err)
		}
		useCache = r.Method == "GET" && ttl > 0 && e.stash != nil

		muxVals := mux.Vars(r)
		params := make([]string, len(e.muxVars))
		for i, v := range e.muxVars {
			params[i] = muxVals[v]
		}

		if e.isStdHttpHandler {
			//TODO: caching of standard handler
			e.caller.Do([]reflect.Value{reflect.ValueOf(w), reflect.ValueOf(r)})
		} else {
			ref := convertToType(params, e.caller.inpParams)
			if e.needsJarInput {
				ref = append(ref, reflect.ValueOf(NewJar(r)))
			}

			if useCache {
				val, err = e.stash.Get(r.RequestURI)
				if err == nil {
					cacheHit = true
					// fmt.Print(".")
					out = decomposeCachedValues(val, e.caller.outParams)
				} else {
					out = e.caller.Do(ref)
					if len(out) == 2 && e.caller.outParams[0] == "int" {
						code := out[0].Int()
						if code < 200 || code > 299 {
							useCache = false
						}
					}
					if useCache {
						bytes := prepareForCaching(out, e.caller.outParams)
						e.stash.Set(r.RequestURI, bytes, ttl)
						// fmt.Print(":", len(bytes), r.RequestURI)
					}

				}
			} else {
				out = e.caller.Do(ref)
				// fmt.Print("!")
			}
			writeOutput(w, e.caller.outParams, out, e.info.Pretty)
		}
	}
}

func prepareForCaching(r []reflect.Value, outputParams []string) []byte {

	var err error
	buf := new(bytes.Buffer)
	encd := json.NewEncoder(buf)

	for i, _ := range r {
		switch outputParams[i] {
		case "int":
			err = encd.Encode(r[i].Int())
			panik.On(err)
		case "map":
			err = encd.Encode(r[i].Interface().(map[string]interface{}))
			panik.On(err)
		case "string":
			err = encd.Encode(r[i].String())
			panik.On(err)
		case "*st:github.com/tolexo/aqua.Sac":
			err = encd.Encode(r[i].Elem().Interface().(Sac).Data)
		default:
			panic("Unknown type of output to be sent to endpoint cache: " + outputParams[i])
		}
	}

	return buf.Bytes()
}

func decomposeCachedValues(data []byte, outputParams []string) []reflect.Value {

	var err error
	buf := bytes.NewBuffer(data)
	decd := json.NewDecoder(buf)
	out := make([]reflect.Value, len(outputParams))

	for i, o := range outputParams {
		switch o {
		case "int":
			var j int
			err = decd.Decode(&j)
			panik.On(err)
			out[i] = reflect.ValueOf(j)
		case "map":
			var m map[string]interface{}
			err = decd.Decode(&m)
			panik.On(err)
			out[i] = reflect.ValueOf(m)
		case "string":
			var s string
			err = decd.Decode(&s)
			panik.On(err)
			out[i] = reflect.ValueOf(s)
		case "*st:github.com/tolexo/aqua.Sac":
			var m map[string]interface{}
			err = decd.Decode(&m)
			panik.On(err)
			s := NewSac()
			s.Data = m
			out[i] = reflect.ValueOf(s)
		default:
			panic("Unknown type of output to be decoded from endpoint cache:" + o)
		}
	}

	return out

}
<|endoftext|>


<commit_before>// Copyright 2016, RadiantBlue Technologies, Inc.
//
// 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 workflow

import (
	"errors"
	"fmt"
	"log"
	"time"

	uuidpkg "github.com/pborman/uuid"
	"github.com/venicegeo/pz-gocommon/gocommon"
)

//-TRIGGER----------------------------------------------------------------------

// TriggerDBMapping is the name of the Elasticsearch type to which Triggers are added
const TriggerDBMapping string = "Trigger"

// TriggerIndexSettings is the mapping for the "trigger" index in Elasticsearch
const TriggerIndexSettings = `
{
	"mappings": {
		"Trigger": {
			"properties": {
				"triggerId": {
					"type": "string",
					"index": "not_analyzed"
				},
				"title": {
					"type": "string",
					"index": "not_analyzed"
				},
				"createdOn": {
					"type": "date"
				},
				"createdBy": {
					"type": "string",
					"index": "not_analyzed"
				},
				"eventTypeId": {
					"type": "string",
					"index": "not_analyzed"
				},
				"enabled": {
					"type": "boolean",
					"index": "not_analyzed"
				},
				"condition": {
					"dynamic": true,
					"properties": {}
				},
				"job": {
					"properties": {
						"createdBy": {
							"type": "string",
							"index": "not_analyzed"
						},
						"jobType": {
							"dynamic": true,
							"properties": {}
						}
					}
				},
				"percolationId": {
					"type": "string",
					"index": "not_analyzed"
				}
			}
		}
	}
}
`

type JobRequest struct {
	CreatedBy string  `json:"createdBy"`
	JobType   JobType `json:"jobType" binding:"required"`
}

type JobType struct {
	Data map[string]interface{} `json:"data" binding:"required"`
	Type string                 `json:"type" binding:"required"`
}

// Trigger does something when the and'ed set of Conditions all are true
// Events are the results of the Conditions queries
// Job is the JobMessage to submit back to Pz
type Trigger struct {
	TriggerID     piazza.Ident           `json:"triggerId"`
	Name          string                 `json:"name" binding:"required"`
	EventTypeID   piazza.Ident           `json:"eventTypeId" binding:"required"`
	Condition     map[string]interface{} `json:"condition" binding:"required"`
	Job           JobRequest             `json:"job" binding:"required"`
	PercolationID piazza.Ident           `json:"percolationId"`
	CreatedBy     string                 `json:"createdBy"`
	CreatedOn     time.Time              `json:"createdOn"`
	Enabled       bool                   `json:"enabled"`
}
type TriggerUpdate struct {
	Enabled bool `json:"enabled"`
}

// TriggerList is a list of triggers
type TriggerList []Trigger

//-EVENT------------------------------------------------------------------------

// EventIndexSettings is the mapping for the "events" index in Elasticsearch
const EventIndexSettings = `
{
	"settings": {
		"index.mapping.coerce": false,
		"index.version.created": 2010299
	},
	"mappings": {
		"_default_": {
			"dynamic": "false",
			"properties": {
				"eventTypeId": {
					"type": "string",
					"index": "not_analyzed"
				},
				"eventId": {
					"type": "string",
					"index": "not_analyzed"
				},
				"data": {
					"properties": {}
				},
				"createdBy": {
					"type": "string",
					"index": "not_analyzed"
				},
				"createdOn": {
					"type": "date"
				},
				"cronSchedule": {
					"type": "string",
					"index": "not_analyzed"
				}
			}
		}
	}
}
`

// An Event is posted by some source (service, user, etc) to indicate Something Happened
// Data is specific to the event type
type Event struct {
	EventID      piazza.Ident           `json:"eventId"`
	EventTypeID  piazza.Ident           `json:"eventTypeId" binding:"required"`
	Data         map[string]interface{} `json:"data"`
	CreatedBy    string                 `json:"createdBy"`
	CreatedOn    time.Time              `json:"createdOn"`
	CronSchedule string                 `json:"cronSchedule"`
}

// EventList is a list of events
type EventList []Event

//-EVENTTYPE--------------------------------------------------------------------

// EventTypeDBMapping is the name of the Elasticsearch type to which Events are added
const EventTypeDBMapping string = "EventType"

// EventTypeIndexSettings is the mapping for the "eventtypes" index in Elasticsearch
const EventTypeIndexSettings = `
{
	"mappings": {
		"EventType": {
			"properties": {
				"eventTypeId": {
					"type": "string",
					"index": "not_analyzed"
				},
				"name": {
					"type": "string",
					"index": "not_analyzed"
				},
				"createdOn": {
					"type": "date"
				},
				"createdBy": {
					"type": "string",
					"index": "not_analyzed"
				},
				"mapping": {
					"dynamic": true,
					"properties": {}
				}
			}
		}
	}
}
`

// EventType describes an Event that is to be sent to workflow by a client or service
type EventType struct {
	EventTypeID piazza.Ident           `json:"eventTypeId"`
	Name        string                 `json:"name" binding:"required"`
	Mapping     map[string]interface{} `json:"mapping" binding:"required"`
	CreatedBy   string                 `json:"createdBy"`
	CreatedOn   time.Time              `json:"createdOn"`
}

// EventTypeList is a list of EventTypes
type EventTypeList []EventType

//-ALERT------------------------------------------------------------------------

// AlertDBMapping is the name of the Elasticsearch type to which Alerts are added
const AlertDBMapping string = "Alert"

// AlertIndexSettings are the default settings for our Elasticsearch alerts index
// Explanation:
//   "index": "not_analyzed"
//     This means that these properties are not analyzed by Elasticsearch.
//     Previously, these ids were analyzed by ES and thus broken up into chunks;
//     in the case of a UUID this would happen via break-up by the "-" character.
//     For example, the UUID "ab3142cd-1a8e-44f8-6a01-5ce8a9328fb2" would be broken
//     into "ab3142cd", "1a8e", "44f8", "6a01" and "5ce8a9328fb2", and queries would
//     match on all of these separate strings, which was undesired behavior.
const AlertIndexSettings = `
{
	"mappings": {
		"Alert": {
			"properties": {
				"alertId": {
					"type": "string",
					"index": "not_analyzed"
				},
				"triggerId": {
					"type": "string",
					"index": "not_analyzed"
				},
				"jobId": {
					"type": "string",
					"index": "not_analyzed"
				},
				"eventId": {
					"type": "string",
					"index": "not_analyzed"
				},
				"createdBy": {
					"type": "string",
					"index": "not_analyzed"
				},
				"createdOn": {
					"type": "date"
				}
			}
		}
	}
}
`

// Alert is a notification, automatically created when a Trigger happens
type Alert struct {
	AlertID   piazza.Ident `json:"alertId"`
	TriggerID piazza.Ident `json:"triggerId"`
	EventID   piazza.Ident `json:"eventId"`
	JobID     piazza.Ident `json:"jobId"`
	CreatedBy string       `json:"createdBy"`
	CreatedOn time.Time    `json:"createdOn"`
}

type AlertExt struct {
	AlertID   piazza.Ident `json:"alertId"`
	Trigger   Trigger      `json:"trigger" binding:"required"`
	Event     Event        `json:"event" binding:"required"`
	JobID     piazza.Ident `json:"jobId"`
	CreatedBy string       `json:"createdBy"`
	CreatedOn time.Time    `json:"createdOn"`
}

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

type TestElasticsearchBody struct {
	ID    piazza.Ident `json:"id"`
	Value int          `json:"value"`
}

const TestElasticsearchIndexSettings = `
{
	"mappings": {
		"TestElasticsearch": {
			"properties": {
				"id": {
					"type": "string",
					"index": "not_analyzed"
				},
				"value": {
					"type": "integer"
				}
			}
		}
	}
}
`

//-CRON-------------------------------------------------------------------------

const CronIndexSettings = `
{
	"settings": {
		"index.mapping.coerce": false
	},
	"mappings": {
		"Cron": {
			"properties": {
				"eventTypeId": {
					"type": "string",
					"index": "not_analyzed"
				},
				"eventId": {
					"type": "string",
					"index": "not_analyzed"
				},
				"data": {
					"properties": {}
				},
				"createdBy": {
					"type": "string",
					"index": "not_analyzed"
				},
				"createdOn": {
					"type": "date"
				},
				"cronSchedule": {
					"type": "string",
					"index": "not_analyzed"
				}
			}
		}
	}
}
`

const cronDBMapping = "Cron"

//-- Stats ------------------------------------------------------------

type Stats struct {
	CreatedOn        time.Time `json:"createdOn"`
	NumEventTypes    int       `json:"numEventTypes"`
	NumEvents        int       `json:"numEvents"`
	NumTriggers      int       `json:"numTriggers"`
	NumAlerts        int       `json:"numAlerts"`
	NumTriggeredJobs int       `json:"numTriggeredJobs"`
}

func (stats *Stats) incrCounter(counter *int) {
	*counter++
}

func (stats *Stats) IncrEventTypes() {
	stats.incrCounter(&stats.NumEventTypes)
}

func (stats *Stats) IncrEvents() {
	stats.incrCounter(&stats.NumEvents)
}

func (stats *Stats) IncrTriggers() {
	stats.incrCounter(&stats.NumTriggers)
}

func (stats *Stats) IncrAlerts() {
	stats.incrCounter(&stats.NumAlerts)
}

func (stats *Stats) IncrTriggerJobs() {
	stats.incrCounter(&stats.NumTriggeredJobs)
}

//-UTILITY----------------------------------------------------------------------

// LoggedError logs the error's message and creates an error
func LoggedError(mssg string, args ...interface{}) error {
	str := fmt.Sprintf(mssg, args...)
	log.Print(str)
	return errors.New(str)
}

// isUUID checks to see if the UUID is valid
func isUUID(uuid piazza.Ident) bool {
	return uuidpkg.Parse(uuid.String()) != nil
}

//-INIT-------------------------------------------------------------------------

func init() {
	piazza.JsonResponseDataTypes["*workflow.EventType"] = "eventtype"
	piazza.JsonResponseDataTypes["[]workflow.EventType"] = "eventtype-list"
	piazza.JsonResponseDataTypes["*workflow.Event"] = "event"
	piazza.JsonResponseDataTypes["[]workflow.Event"] = "event-list"
	piazza.JsonResponseDataTypes["*workflow.Trigger"] = "trigger"
	piazza.JsonResponseDataTypes["[]workflow.Trigger"] = "trigger-list"
	piazza.JsonResponseDataTypes["*workflow.Alert"] = "alert"
	piazza.JsonResponseDataTypes["[]workflow.Alert"] = "alert-list"
	piazza.JsonResponseDataTypes["[]workflow.AlertExt"] = "alertext-list"
	piazza.JsonResponseDataTypes["workflow.Stats"] = "workflowstats"
	piazza.JsonResponseDataTypes["*workflow.TestElasticsearchBody"] = "testelasticsearch"
	piazza.JsonResponseDataTypes["[]workflow.TestElasticsearchBody"] = "testelasticsearch-list"
}
<commit_msg>debugging<commit_after>// Copyright 2016, RadiantBlue Technologies, Inc.
//
// 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 workflow

import (
	"errors"
	"fmt"
	"log"
	"time"

	uuidpkg "github.com/pborman/uuid"
	"github.com/venicegeo/pz-gocommon/gocommon"
)

//-TRIGGER----------------------------------------------------------------------

// TriggerDBMapping is the name of the Elasticsearch type to which Triggers are added
const TriggerDBMapping string = "Trigger"

// TriggerIndexSettings is the mapping for the "trigger" index in Elasticsearch
const TriggerIndexSettings = `
{
	"mappings": {
		"Trigger": {
			"properties": {
				"triggerId": {
					"type": "string",
					"index": "not_analyzed"
				},
				"title": {
					"type": "string",
					"index": "not_analyzed"
				},
				"createdOn": {
					"type": "date"
				},
				"createdBy": {
					"type": "string",
					"index": "not_analyzed"
				},
				"eventTypeId": {
					"type": "string",
					"index": "not_analyzed"
				},
				"enabled": {
					"type": "boolean",
					"index": "not_analyzed"
				},
				"condition": {
					"dynamic": true,
					"properties": {}
				},
				"job": {
					"properties": {
						"createdBy": {
							"type": "string",
							"index": "not_analyzed"
						},
						"jobType": {
							"dynamic": true,
							"properties": {}
						}
					}
				},
				"percolationId": {
					"type": "string",
					"index": "not_analyzed"
				}
			}
		}
	}
}
`

type JobRequest struct {
	CreatedBy string  `json:"createdBy"`
	JobType   JobType `json:"jobType" binding:"required"`
}

type JobType struct {
	Data map[string]interface{} `json:"data" binding:"required"`
	Type string                 `json:"type" binding:"required"`
}

// Trigger does something when the and'ed set of Conditions all are true
// Events are the results of the Conditions queries
// Job is the JobMessage to submit back to Pz
type Trigger struct {
	TriggerID     piazza.Ident           `json:"triggerId"`
	Name          string                 `json:"name" binding:"required"`
	EventTypeID   piazza.Ident           `json:"eventTypeId" binding:"required"`
	Condition     map[string]interface{} `json:"condition" binding:"required"`
	Job           JobRequest             `json:"job" binding:"required"`
	PercolationID piazza.Ident           `json:"percolationId"`
	CreatedBy     string                 `json:"createdBy"`
	CreatedOn     time.Time              `json:"createdOn"`
	Enabled       bool                   `json:"enabled"`
}
type TriggerUpdate struct {
	Enabled bool `json:"enabled"`
}

// TriggerList is a list of triggers
type TriggerList []Trigger

//-EVENT------------------------------------------------------------------------

// EventIndexSettings is the mapping for the "events" index in Elasticsearch
const EventIndexSettings = `
{
	"settings": {
		"index.mapping.coerce": false,
		"index.version.created": 2010299
	},
	"mappings": {
		"_default_": {
			"dynamic": "false",
			"properties": {
				"eventTypeId": {
					"type": "string",
					"index": "not_analyzed"
				},
				"eventId": {
					"type": "string",
					"index": "not_analyzed"
				},
				"data": {
					"properties": {}
				},
				"createdBy": {
					"type": "string",
					"index": "not_analyzed"
				},
				"createdOn": {
					"type": "date"
				},
				"cronSchedule": {
					"type": "string",
					"index": "not_analyzed"
				}
			}
		}
	}
}
`

// An Event is posted by some source (service, user, etc) to indicate Something Happened
// Data is specific to the event type
type Event struct {
	EventID      piazza.Ident           `json:"eventId"`
	EventTypeID  piazza.Ident           `json:"eventTypeId" binding:"required"`
	Data         map[string]interface{} `json:"data"`
	CreatedBy    string                 `json:"createdBy"`
	CreatedOn    time.Time              `json:"createdOn"`
	CronSchedule string                 `json:"cronSchedule"`
}

// EventList is a list of events
type EventList []Event

//-EVENTTYPE--------------------------------------------------------------------

// EventTypeDBMapping is the name of the Elasticsearch type to which Events are added
const EventTypeDBMapping string = "EventType"

// EventTypeIndexSettings is the mapping for the "eventtypes" index in Elasticsearch
const EventTypeIndexSettings = `
{
	"mappings": {
		"EventType": {
			"properties": {
				"eventTypeId": {
					"type": "string",
					"index": "not_analyzed"
				},
				"name": {
					"type": "string",
					"index": "not_analyzed"
				},
				"createdOn": {
					"type": "date"
				},
				"createdBy": {
					"type": "string",
					"index": "not_analyzed"
				},
				"mapping": {
					"dynamic": true,
					"properties": {}
				}
			}
		}
	}
}
`

// EventType describes an Event that is to be sent to workflow by a client or service
type EventType struct {
	EventTypeID piazza.Ident           `json:"eventTypeId"`
	Name        string                 `json:"name" binding:"required"`
	Mapping     map[string]interface{} `json:"mapping" binding:"required"`
	CreatedBy   string                 `json:"createdBy"`
	CreatedOn   time.Time              `json:"createdOn"`
}

// EventTypeList is a list of EventTypes
type EventTypeList []EventType

//-ALERT------------------------------------------------------------------------

// AlertDBMapping is the name of the Elasticsearch type to which Alerts are added
const AlertDBMapping string = "Alert"

// AlertIndexSettings are the default settings for our Elasticsearch alerts index
// Explanation:
//   "index": "not_analyzed"
//     This means that these properties are not analyzed by Elasticsearch.
//     Previously, these ids were analyzed by ES and thus broken up into chunks;
//     in the case of a UUID this would happen via break-up by the "-" character.
//     For example, the UUID "ab3142cd-1a8e-44f8-6a01-5ce8a9328fb2" would be broken
//     into "ab3142cd", "1a8e", "44f8", "6a01" and "5ce8a9328fb2", and queries would
//     match on all of these separate strings, which was undesired behavior.
const AlertIndexSettings = `
{
	"mappings": {
		"Alert": {
			"properties": {
				"alertId": {
					"type": "string",
					"index": "not_analyzed"
				},
				"triggerId": {
					"type": "string",
					"index": "not_analyzed"
				},
				"jobId": {
					"type": "string",
					"index": "not_analyzed"
				},
				"eventId": {
					"type": "string",
					"index": "not_analyzed"
				},
				"createdBy": {
					"type": "string",
					"index": "not_analyzed"
				},
				"createdOn": {
					"type": "date"
				}
			}
		}
	}
}
`

// Alert is a notification, automatically created when a Trigger happens
type Alert struct {
	AlertID   piazza.Ident `json:"alertId"`
	TriggerID piazza.Ident `json:"triggerId"`
	EventID   piazza.Ident `json:"eventId"`
	JobID     piazza.Ident `json:"jobId"`
	CreatedBy string       `json:"createdBy"`
	CreatedOn time.Time    `json:"createdOn"`
}

type AlertExt struct {
	AlertID   piazza.Ident `json:"alertId"`
	Trigger   Trigger      `json:"trigger" binding:"required"`
	Event     Event        `json:"event" binding:"required"`
	JobID     piazza.Ident `json:"jobId"`
	CreatedBy string       `json:"createdBy"`
	CreatedOn time.Time    `json:"createdOn"`
}

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

type TestElasticsearchBody struct {
	ID    piazza.Ident `json:"id"`
	Value int          `json:"value"`
}

const TestElasticsearchIndexSettings = `
{
	"mappings": {
		"TestElasticsearch": {
			"properties": {
			}
		}
	}
}
`

/*
	"id": {
		"type": "string",
		"index": "not_analyzed"
	},
	"value": {
		"type": "integer"
	}
*/

//-CRON-------------------------------------------------------------------------

const CronIndexSettings = `
{
	"settings": {
		"index.mapping.coerce": false
	},
	"mappings": {
		"Cron": {
			"properties": {
				"eventTypeId": {
					"type": "string",
					"index": "not_analyzed"
				},
				"eventId": {
					"type": "string",
					"index": "not_analyzed"
				},
				"data": {
					"properties": {}
				},
				"createdBy": {
					"type": "string",
					"index": "not_analyzed"
				},
				"createdOn": {
					"type": "date"
				},
				"cronSchedule": {
					"type": "string",
					"index": "not_analyzed"
				}
			}
		}
	}
}
`

const cronDBMapping = "Cron"

//-- Stats ------------------------------------------------------------

type Stats struct {
	CreatedOn        time.Time `json:"createdOn"`
	NumEventTypes    int       `json:"numEventTypes"`
	NumEvents        int       `json:"numEvents"`
	NumTriggers      int       `json:"numTriggers"`
	NumAlerts        int       `json:"numAlerts"`
	NumTriggeredJobs int       `json:"numTriggeredJobs"`
}

func (stats *Stats) incrCounter(counter *int) {
	*counter++
}

func (stats *Stats) IncrEventTypes() {
	stats.incrCounter(&stats.NumEventTypes)
}

func (stats *Stats) IncrEvents() {
	stats.incrCounter(&stats.NumEvents)
}

func (stats *Stats) IncrTriggers() {
	stats.incrCounter(&stats.NumTriggers)
}

func (stats *Stats) IncrAlerts() {
	stats.incrCounter(&stats.NumAlerts)
}

func (stats *Stats) IncrTriggerJobs() {
	stats.incrCounter(&stats.NumTriggeredJobs)
}

//-UTILITY----------------------------------------------------------------------

// LoggedError logs the error's message and creates an error
func LoggedError(mssg string, args ...interface{}) error {
	str := fmt.Sprintf(mssg, args...)
	log.Print(str)
	return errors.New(str)
}

// isUUID checks to see if the UUID is valid
func isUUID(uuid piazza.Ident) bool {
	return uuidpkg.Parse(uuid.String()) != nil
}

//-INIT-------------------------------------------------------------------------

func init() {
	piazza.JsonResponseDataTypes["*workflow.EventType"] = "eventtype"
	piazza.JsonResponseDataTypes["[]workflow.EventType"] = "eventtype-list"
	piazza.JsonResponseDataTypes["*workflow.Event"] = "event"
	piazza.JsonResponseDataTypes["[]workflow.Event"] = "event-list"
	piazza.JsonResponseDataTypes["*workflow.Trigger"] = "trigger"
	piazza.JsonResponseDataTypes["[]workflow.Trigger"] = "trigger-list"
	piazza.JsonResponseDataTypes["*workflow.Alert"] = "alert"
	piazza.JsonResponseDataTypes["[]workflow.Alert"] = "alert-list"
	piazza.JsonResponseDataTypes["[]workflow.AlertExt"] = "alertext-list"
	piazza.JsonResponseDataTypes["workflow.Stats"] = "workflowstats"
	piazza.JsonResponseDataTypes["*workflow.TestElasticsearchBody"] = "testelasticsearch"
	piazza.JsonResponseDataTypes["[]workflow.TestElasticsearchBody"] = "testelasticsearch-list"
}
<|endoftext|>


<commit_before>package manifest

import (
	"generic"
	"github.com/cloudfoundry/gamble"
	"io"
	"io/ioutil"
)

func Parse(reader io.Reader) (yamlMap generic.Map, err error) {
	yamlBytes, err := ioutil.ReadAll(reader)
	if err != nil {
		return
	}

	document, err := gamble.Parse(string(yamlBytes))
	if err != nil {
		return
	}

	yamlMap = generic.NewMap(document)
	return
}

func ParseToManifest(reader io.Reader) (m *Manifest, errs ManifestErrors) {
	mapp, err := Parse(reader)
	if err != nil {
		errs = append(errs, err)
		return
	}

	return NewManifest(mapp)
}
<commit_msg>Remove some dead code<commit_after>package manifest

import (
	"generic"
	"github.com/cloudfoundry/gamble"
	"io"
	"io/ioutil"
)

func Parse(reader io.Reader) (yamlMap generic.Map, err error) {
	yamlBytes, err := ioutil.ReadAll(reader)
	if err != nil {
		return
	}

	document, err := gamble.Parse(string(yamlBytes))
	if err != nil {
		return
	}

	yamlMap = generic.NewMap(document)
	return
}
<|endoftext|>


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

// Addr2line is a minimal simulation of the GNU addr2line tool,
// just enough to support pprof.
//
// Usage:
//	go tool addr2line binary
//
// Addr2line reads hexadecimal addresses, one per line and without a 0x prefix,
// from standard input. For each input address, addr2line prints two output lines,
// first the name of the function containing the address and second the file:line
// of the source code corresponding to that address.
//
// This tool is intended for use only by pprof; its interface may change or
// it may be deleted entirely in future releases.
package main

import (
	"bufio"
	"debug/elf"
	"debug/gosym"
	"debug/macho"
	"debug/pe"
	"flag"
	"fmt"
	"log"
	"os"
	"strconv"
	"strings"
)

func printUsage(w *os.File) {
	fmt.Fprintf(w, "usage: addr2line binary\n")
	fmt.Fprintf(w, "reads addresses from standard input and writes two lines for each:\n")
	fmt.Fprintf(w, "\tfunction name\n")
	fmt.Fprintf(w, "\tfile:line\n")
}

func usage() {
	printUsage(os.Stderr)
	os.Exit(2)
}

func main() {
	log.SetFlags(0)
	log.SetPrefix("addr2line: ")

	// pprof expects this behavior when checking for addr2line
	if len(os.Args) > 1 && os.Args[1] == "--help" {
		printUsage(os.Stdout)
		os.Exit(0)
	}

	flag.Usage = usage
	flag.Parse()
	if flag.NArg() != 1 {
		usage()
	}

	f, err := os.Open(flag.Arg(0))
	if err != nil {
		log.Fatal(err)
	}

	textStart, symtab, pclntab, err := loadTables(f)
	if err != nil {
		log.Fatalf("reading %s: %v", flag.Arg(0), err)
	}

	pcln := gosym.NewLineTable(pclntab, textStart)
	tab, err := gosym.NewTable(symtab, pcln)
	if err != nil {
		log.Fatalf("reading %s: %v", flag.Arg(0), err)
	}

	stdin := bufio.NewScanner(os.Stdin)
	stdout := bufio.NewWriter(os.Stdout)

	for stdin.Scan() {
		p := stdin.Text()
		if strings.Contains(p, ":") {
			// Reverse translate file:line to pc.
			// This was an extension in the old C version of 'go tool addr2line'
			// and is probably not used by anyone, but recognize the syntax.
			// We don't have an implementation.
			fmt.Fprintf(stdout, "!reverse translation not implemented\n")
			continue
		}
		pc, _ := strconv.ParseUint(p, 16, 64)
		file, line, fn := tab.PCToLine(pc)
		name := "?"
		if fn != nil {
			name = fn.Name
		} else {
			file = "?"
			line = 0
		}
		fmt.Fprintf(stdout, "%s\n%s:%d\n", name, file, line)
	}
	stdout.Flush()
}

func loadTables(f *os.File) (textStart uint64, symtab, pclntab []byte, err error) {
	if obj, err := elf.NewFile(f); err == nil {
		if sect := obj.Section(".text"); sect != nil {
			textStart = sect.Addr
		}
		if sect := obj.Section(".gosymtab"); sect != nil {
			if symtab, err = sect.Data(); err != nil {
				return 0, nil, nil, err
			}
		}
		if sect := obj.Section(".gopclntab"); sect != nil {
			if pclntab, err = sect.Data(); err != nil {
				return 0, nil, nil, err
			}
		}
		return textStart, symtab, pclntab, nil
	}

	if obj, err := macho.NewFile(f); err == nil {
		if sect := obj.Section("__text"); sect != nil {
			textStart = sect.Addr
		}
		if sect := obj.Section("__gosymtab"); sect != nil {
			if symtab, err = sect.Data(); err != nil {
				return 0, nil, nil, err
			}
		}
		if sect := obj.Section("__gopclntab"); sect != nil {
			if pclntab, err = sect.Data(); err != nil {
				return 0, nil, nil, err
			}
		}
		return textStart, symtab, pclntab, nil
	}

	if obj, err := pe.NewFile(f); err == nil {
		if sect := obj.Section(".text"); sect != nil {
			textStart = uint64(sect.VirtualAddress)
		}
		if pclntab, err = loadPETable(obj, "pclntab", "epclntab"); err != nil {
			return 0, nil, nil, err
		}
		if symtab, err = loadPETable(obj, "symtab", "esymtab"); err != nil {
			return 0, nil, nil, err
		}
		return textStart, symtab, pclntab, nil
	}

	return 0, nil, nil, fmt.Errorf("unrecognized binary format")
}

func findPESymbol(f *pe.File, name string) (*pe.Symbol, error) {
	for _, s := range f.Symbols {
		if s.Name != name {
			continue
		}
		if s.SectionNumber <= 0 {
			return nil, fmt.Errorf("symbol %s: invalid section number %d", name, s.SectionNumber)
		}
		if len(f.Sections) < int(s.SectionNumber) {
			return nil, fmt.Errorf("symbol %s: section number %d is larger than max %d", name, s.SectionNumber, len(f.Sections))
		}
		return s, nil
	}
	return nil, fmt.Errorf("no %s symbol found", name)
}

func loadPETable(f *pe.File, sname, ename string) ([]byte, error) {
	ssym, err := findPESymbol(f, sname)
	if err != nil {
		return nil, err
	}
	esym, err := findPESymbol(f, ename)
	if err != nil {
		return nil, err
	}
	if ssym.SectionNumber != esym.SectionNumber {
		return nil, fmt.Errorf("%s and %s symbols must be in the same section", sname, ename)
	}
	sect := f.Sections[ssym.SectionNumber-1]
	data, err := sect.Data()
	if err != nil {
		return nil, err
	}
	return data[ssym.Value:esym.Value], nil
}
<commit_msg>cmd/addr2line: accept optional "0x" prefix for addresses.<commit_after>// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Addr2line is a minimal simulation of the GNU addr2line tool,
// just enough to support pprof.
//
// Usage:
//	go tool addr2line binary
//
// Addr2line reads hexadecimal addresses, one per line and with optional 0x prefix,
// from standard input. For each input address, addr2line prints two output lines,
// first the name of the function containing the address and second the file:line
// of the source code corresponding to that address.
//
// This tool is intended for use only by pprof; its interface may change or
// it may be deleted entirely in future releases.
package main

import (
	"bufio"
	"debug/elf"
	"debug/gosym"
	"debug/macho"
	"debug/pe"
	"flag"
	"fmt"
	"log"
	"os"
	"strconv"
	"strings"
)

func printUsage(w *os.File) {
	fmt.Fprintf(w, "usage: addr2line binary\n")
	fmt.Fprintf(w, "reads addresses from standard input and writes two lines for each:\n")
	fmt.Fprintf(w, "\tfunction name\n")
	fmt.Fprintf(w, "\tfile:line\n")
}

func usage() {
	printUsage(os.Stderr)
	os.Exit(2)
}

func main() {
	log.SetFlags(0)
	log.SetPrefix("addr2line: ")

	// pprof expects this behavior when checking for addr2line
	if len(os.Args) > 1 && os.Args[1] == "--help" {
		printUsage(os.Stdout)
		os.Exit(0)
	}

	flag.Usage = usage
	flag.Parse()
	if flag.NArg() != 1 {
		usage()
	}

	f, err := os.Open(flag.Arg(0))
	if err != nil {
		log.Fatal(err)
	}

	textStart, symtab, pclntab, err := loadTables(f)
	if err != nil {
		log.Fatalf("reading %s: %v", flag.Arg(0), err)
	}

	pcln := gosym.NewLineTable(pclntab, textStart)
	tab, err := gosym.NewTable(symtab, pcln)
	if err != nil {
		log.Fatalf("reading %s: %v", flag.Arg(0), err)
	}

	stdin := bufio.NewScanner(os.Stdin)
	stdout := bufio.NewWriter(os.Stdout)

	for stdin.Scan() {
		p := stdin.Text()
		if strings.Contains(p, ":") {
			// Reverse translate file:line to pc.
			// This was an extension in the old C version of 'go tool addr2line'
			// and is probably not used by anyone, but recognize the syntax.
			// We don't have an implementation.
			fmt.Fprintf(stdout, "!reverse translation not implemented\n")
			continue
		}
		pc, _ := strconv.ParseUint(strings.TrimPrefix(p, "0x"), 16, 64)
		file, line, fn := tab.PCToLine(pc)
		name := "?"
		if fn != nil {
			name = fn.Name
		} else {
			file = "?"
			line = 0
		}
		fmt.Fprintf(stdout, "%s\n%s:%d\n", name, file, line)
	}
	stdout.Flush()
}

func loadTables(f *os.File) (textStart uint64, symtab, pclntab []byte, err error) {
	if obj, err := elf.NewFile(f); err == nil {
		if sect := obj.Section(".text"); sect != nil {
			textStart = sect.Addr
		}
		if sect := obj.Section(".gosymtab"); sect != nil {
			if symtab, err = sect.Data(); err != nil {
				return 0, nil, nil, err
			}
		}
		if sect := obj.Section(".gopclntab"); sect != nil {
			if pclntab, err = sect.Data(); err != nil {
				return 0, nil, nil, err
			}
		}
		return textStart, symtab, pclntab, nil
	}

	if obj, err := macho.NewFile(f); err == nil {
		if sect := obj.Section("__text"); sect != nil {
			textStart = sect.Addr
		}
		if sect := obj.Section("__gosymtab"); sect != nil {
			if symtab, err = sect.Data(); err != nil {
				return 0, nil, nil, err
			}
		}
		if sect := obj.Section("__gopclntab"); sect != nil {
			if pclntab, err = sect.Data(); err != nil {
				return 0, nil, nil, err
			}
		}
		return textStart, symtab, pclntab, nil
	}

	if obj, err := pe.NewFile(f); err == nil {
		if sect := obj.Section(".text"); sect != nil {
			textStart = uint64(sect.VirtualAddress)
		}
		if pclntab, err = loadPETable(obj, "pclntab", "epclntab"); err != nil {
			return 0, nil, nil, err
		}
		if symtab, err = loadPETable(obj, "symtab", "esymtab"); err != nil {
			return 0, nil, nil, err
		}
		return textStart, symtab, pclntab, nil
	}

	return 0, nil, nil, fmt.Errorf("unrecognized binary format")
}

func findPESymbol(f *pe.File, name string) (*pe.Symbol, error) {
	for _, s := range f.Symbols {
		if s.Name != name {
			continue
		}
		if s.SectionNumber <= 0 {
			return nil, fmt.Errorf("symbol %s: invalid section number %d", name, s.SectionNumber)
		}
		if len(f.Sections) < int(s.SectionNumber) {
			return nil, fmt.Errorf("symbol %s: section number %d is larger than max %d", name, s.SectionNumber, len(f.Sections))
		}
		return s, nil
	}
	return nil, fmt.Errorf("no %s symbol found", name)
}

func loadPETable(f *pe.File, sname, ename string) ([]byte, error) {
	ssym, err := findPESymbol(f, sname)
	if err != nil {
		return nil, err
	}
	esym, err := findPESymbol(f, ename)
	if err != nil {
		return nil, err
	}
	if ssym.SectionNumber != esym.SectionNumber {
		return nil, fmt.Errorf("%s and %s symbols must be in the same section", sname, ename)
	}
	sect := f.Sections[ssym.SectionNumber-1]
	data, err := sect.Data()
	if err != nil {
		return nil, err
	}
	return data[ssym.Value:esym.Value], nil
}
<|endoftext|>