<commit_before>package batchproducer

import (
	"errors"
	"log"
	"sync"
	"time"

	"github.com/timehop/go-kinesis"
)

// BatchProducer collects records individually and then sends them to Kinesis in
// batches in the background using PutRecords, with retries.
// A BatchProducer will do nothing until Start is called.
type BatchProducer interface {
	// Start starts the main goroutine. No need to call it using `go`.
	Start() error

	// Stop signals the main goroutine to finish. Once this is called, Add will immediately start
	// returning errors (unless and until Start is called again). Stop will block until
	// all remaining records in the buffer have been sent.
	Stop() error

	// Add might block if the BatchProducer has a buffer and the buffer is full.
	// In order to prevent filling the buffer and eventually blocking indefinitely,
	// Add will fail and return an error if the BatchProducer is stopped or stopping. Note
	// that it’s critical to check the return value because the BatchProducer could have
	// died in the background due to a panic (or something).
	Add(data []byte, partitionKey string) error

	// Setters
	SetMaxAttemptsPerRecord(int) error
	SetStatReceiver(BatchStatReceiver) error

	// This interval will be used to make a *best effort* attempt to send stats *approximately*
	// when this interval elapses. There’s no guarantee, however, since the main goroutine is
	// used to send the stats and therefore there may be some skew.
	SetStatInterval(time.Duration) error
}

// BatchStatReceiver defines an object that can accept stats.
type BatchStatReceiver interface {
	// Receive will be called by the main BatchProducer goroutine so it will block all batches from being
	// sent, so make sure it is either very fast or never blocks at all!
	Receive(BatchStat)
}

// BatchStat is a kind of a snapshot of activity and happenings. Some of its fields represent
// "moment-in-time" values e.g. BufferSize is the size of the buffer at the moment the Stat is
// sent. Other fields are cumulative since the last Stat, i.e. ErrorsSinceLastStat.
type BatchStat struct {
	// Moment-in-time stats
	BufferSize int

	// Cumulative stats
	KinesisErrorsSinceLastStat           int
	RecordsSentSuccessfullySinceLastStat int
	RecordsDroppedSinceLastStat          int
}

// BatchingKinesisClient is a subset of KinesisClient to ease mocking.
type BatchingKinesisClient interface {
	PutRecords(args *kinesis.RequestArgs) (resp *kinesis.PutRecordsResp, err error)
}

// New creates and returns a BatchProducer that will do nothing until its Start method is called.
// Once it is started, it will flush a batch to Kinesis whenever either
// the flushInterval occurs (if flushInterval > 0) or the batchSize is reached,
// whichever happens first.
// `bufferSize` is the size of the buffer that stores records before they are sent to the Kinesis
// stream. If the number of records in the buffer is equal to or greater than bufferSize then the
// Add method will block.
// `batchSize` is the max number of records in each batch request sent to Kinesis.
// TODO: this is too many args. Maybe instead have some defaults and add some setter methods for the less critical
// params
func New(
	client BatchingKinesisClient,
	streamName string,
	bufferSize int,
	flushInterval time.Duration,
	batchSize int,
	logger *log.Logger,
) (BatchProducer, error) {
	if batchSize < 1 || batchSize > 500 {
		return nil, errors.New("batchSize must be between 1 and 500 inclusive")
	}

	if bufferSize < batchSize && flushInterval <= 0 {
		return nil, errors.New("If bufferSize < batchSize && flushInterval <= 0 then the buffer will eventually fill up and Add will block forever.")
	}

	if flushInterval > 0 && flushInterval < 50*time.Millisecond {
		return nil, errors.New("Are you crazy?")
	}

	batchProducer := batchProducer{
		client:               client,
		streamName:           streamName,
		flushInterval:        flushInterval,
		batchSize:            batchSize,
		maxAttemptsPerRecord: 10,
		logger:               logger,
		statInterval:         time.Second,
		currentStat:          new(BatchStat),
		records:              make(chan batchRecord, bufferSize),
		stop:                 make(chan interface{}),
	}

	return &batchProducer, nil
}

type batchProducer struct {
	client               BatchingKinesisClient
	streamName           string
	flushInterval        time.Duration
	batchSize            int
	maxAttemptsPerRecord int
	logger               *log.Logger
	running              bool
	runningMu            sync.Mutex
	consecutiveErrors    int
	currentDelay         time.Duration
	statInterval         time.Duration
	statReceiver         BatchStatReceiver
	currentStat          *BatchStat
	records              chan batchRecord
	stop                 chan interface{}
}

type batchRecord struct {
	data         []byte
	partitionKey string
	sendAttempts int
}

// from/for interface BatchProducer
func (b *batchProducer) Add(data []byte, partitionKey string) error {
	if !b.isRunning() {
		return errors.New("Cannot call Add when BatchProducer is not running (to prevent the buffer filling up and Add blocking indefinitely).")
	}
	b.records <- batchRecord{data: data, partitionKey: partitionKey}
	return nil
}

// from/for interface BatchProducer
func (b *batchProducer) Start() error {
	if b.isRunning() {
		return nil
	}

	go b.run()

	for !b.isRunning() {
		time.Sleep(1 * time.Millisecond)
	}

	return nil
}

func (b *batchProducer) run() {
	flushTicker := &time.Ticker{}
	if b.flushInterval > 0 {
		flushTicker = time.NewTicker(b.flushInterval)
		defer flushTicker.Stop()
	}

	statTicker := &time.Ticker{}
	if b.statReceiver != nil && b.statInterval > 0 {
		statTicker = time.NewTicker(b.statInterval)
		defer statTicker.Stop()
	}

	b.setRunning(true)
	// Very important to defer the call to b.setRunning(false) — not just so it’s accurate
	// after b.Stop has been called but also just in case this main goroutine dies in the
	// background — for example because of a panic.
	defer b.setRunning(false)

	for {
		select {
		case <-flushTicker.C:
			b.sendBatch()
		case <-statTicker.C:
			b.sendStats()
		case <-b.stop:
			break
		default:
			if len(b.records) >= b.batchSize {
				b.sendBatch()
			} else {
				time.Sleep(1 * time.Millisecond)
			}
		}
	}
}

// from/for interface BatchProducer
func (b *batchProducer) Stop() error {
	// TODO: Immediately stop accepting new records by Add, then block until all the records in the buffer have been sent
	if b.isRunning() {
		b.stop <- true
	}
	return nil
}

func (b *batchProducer) SetMaxAttemptsPerRecord(v int) error {
	if b.isRunning() {
		return errors.New("Cannot set max attempts per record while BatchProducer is running.")
	}
	b.maxAttemptsPerRecord = v
	return nil
}

func (b *batchProducer) SetStatReceiver(sr BatchStatReceiver) error {
	if b.isRunning() {
		return errors.New("Cannot set BatchStatReceiver while BatchProducer is running.")
	}
	b.statReceiver = sr
	return nil
}

func (b *batchProducer) SetStatInterval(si time.Duration) error {
	if b.isRunning() {
		return errors.New("Cannot set stat interval while BatchProducer is running.")
	}
	b.statInterval = si
	return nil
}

func (b *batchProducer) setRunning(running bool) {
	b.runningMu.Lock()
	defer b.runningMu.Unlock()
	b.running = running
}

func (b *batchProducer) isRunning() bool {
	b.runningMu.Lock()
	defer b.runningMu.Unlock()
	return b.running
}

func (b *batchProducer) sendBatch() {
	if len(b.records) == 0 {
		return
	}

	if b.consecutiveErrors == 1 {
		b.currentDelay = 50 * time.Millisecond
	} else if b.consecutiveErrors > 1 {
		b.currentDelay *= 2
	}

	if b.currentDelay > 0 {
		b.logger.Printf("Delaying the batch by %v because of %v consecutive errors", b.currentDelay, b.consecutiveErrors)
		time.Sleep(b.currentDelay)
	}

	records := b.takeRecordsFromBuffer()
	res, err := b.client.PutRecords(b.recordsToArgs(records))

	if err != nil {
		b.consecutiveErrors++
		b.currentStat.KinesisErrorsSinceLastStat++
		b.logger.Printf("Error occurred when sending PutRecords request to Kinesis stream %v: %v", b.streamName, err)
		b.returnRecordsToBuffer(records)
	} else {
		b.consecutiveErrors = 0
		b.currentDelay = 0
		succeeded := len(records) - res.FailedRecordCount

		b.currentStat.RecordsSentSuccessfullySinceLastStat += succeeded

		if res.FailedRecordCount > 0 {
			b.logger.Printf("Partial success when sending a PutRecords request to Kinesis stream %v: %v succeeded, %v failed. Re-enqueueing failed records.", b.streamName, succeeded, res.FailedRecordCount)
			b.returnSomeFailedRecordsToBuffer(res, records)
		}
	}
}

func (b *batchProducer) takeRecordsFromBuffer() []batchRecord {
	var size int
	bufferLen := len(b.records)
	if bufferLen >= b.batchSize {
		size = b.batchSize
	} else {
		size = bufferLen
	}

	result := make([]batchRecord, size)
	for i := 0; i < size; i++ {
		result[i] = <-b.records
	}
	return result
}

func (b *batchProducer) recordsToArgs(records []batchRecord) *kinesis.RequestArgs {
	args := kinesis.NewArgs()
	args.Add("StreamName", b.streamName)
	for _, record := range records {
		args.AddRecord(record.data, record.partitionKey)
	}
	return args
}

// TODO: perhaps we should use a deque internally as the buffer so we can return records to
// the front of the queue.
func (b *batchProducer) returnRecordsToBuffer(records []batchRecord) {
	for _, record := range records {
		// Not using b.Add because we want to preserve the value of record.sendAttempts.
		b.records <- record
	}
}

func (b *batchProducer) returnSomeFailedRecordsToBuffer(res *kinesis.PutRecordsResp, records []batchRecord) {
	for i, result := range res.Records {
		record := records[i]
		if result.ErrorCode != "" {
			record.sendAttempts++

			if record.sendAttempts < b.maxAttemptsPerRecord {
				b.logger.Printf("Re-enqueueing failed record to buffer for retry. Error code was: '%v' and message was '%v'", result.ErrorCode, result.ErrorMessage)
				// Not using b.Add because we want to preserve the value of record.sendAttempts.
				b.records <- record
			} else {
				b.currentStat.RecordsDroppedSinceLastStat++
				msg := "NOT re-enqueueing failed record for retry, as it has hit %v attempts, " +
					"which is the maximum. Error code was: '%v' and message was '%v'"
				b.logger.Printf(msg, record.sendAttempts, result.ErrorCode, result.ErrorMessage)
			}
		}
	}
}

func (b *batchProducer) sendStats() {
	b.currentStat.BufferSize = len(b.records)

	// TEMP TMEP TEMP
	// fmt.Printf("Sending stat: %+v\n", b.currentStat)

	// I considered running this as a goroutine, but I’m concerned about leaks. So instead, for now,
	// the provider of the BatchStatReceiver must ensure that it is either very fast or non-blocking.
	b.statReceiver.Receive(*b.currentStat)

	b.currentStat = new(BatchStat)
}
<commit_msg>Remove a comment that doesn’t actually make sense<commit_after>package batchproducer

import (
	"errors"
	"log"
	"sync"
	"time"

	"github.com/timehop/go-kinesis"
)

// BatchProducer collects records individually and then sends them to Kinesis in
// batches in the background using PutRecords, with retries.
// A BatchProducer will do nothing until Start is called.
type BatchProducer interface {
	// Start starts the main goroutine. No need to call it using `go`.
	Start() error

	// Stop signals the main goroutine to finish. Once this is called, Add will immediately start
	// returning errors (unless and until Start is called again). Stop will block until
	// all remaining records in the buffer have been sent.
	Stop() error

	// Add might block if the BatchProducer has a buffer and the buffer is full.
	// In order to prevent filling the buffer and eventually blocking indefinitely,
	// Add will fail and return an error if the BatchProducer is stopped or stopping. Note
	// that it’s critical to check the return value because the BatchProducer could have
	// died in the background due to a panic (or something).
	Add(data []byte, partitionKey string) error

	// Setters
	SetMaxAttemptsPerRecord(int) error
	SetStatReceiver(BatchStatReceiver) error

	// This interval will be used to make a *best effort* attempt to send stats *approximately*
	// when this interval elapses. There’s no guarantee, however, since the main goroutine is
	// used to send the stats and therefore there may be some skew.
	SetStatInterval(time.Duration) error
}

// BatchStatReceiver defines an object that can accept stats.
type BatchStatReceiver interface {
	// Receive will be called by the main BatchProducer goroutine so it will block all batches from being
	// sent, so make sure it is either very fast or never blocks at all!
	Receive(BatchStat)
}

// BatchStat is a kind of a snapshot of activity and happenings. Some of its fields represent
// "moment-in-time" values e.g. BufferSize is the size of the buffer at the moment the Stat is
// sent. Other fields are cumulative since the last Stat, i.e. ErrorsSinceLastStat.
type BatchStat struct {
	// Moment-in-time stats
	BufferSize int

	// Cumulative stats
	KinesisErrorsSinceLastStat           int
	RecordsSentSuccessfullySinceLastStat int
	RecordsDroppedSinceLastStat          int
}

// BatchingKinesisClient is a subset of KinesisClient to ease mocking.
type BatchingKinesisClient interface {
	PutRecords(args *kinesis.RequestArgs) (resp *kinesis.PutRecordsResp, err error)
}

// New creates and returns a BatchProducer that will do nothing until its Start method is called.
// Once it is started, it will flush a batch to Kinesis whenever either
// the flushInterval occurs (if flushInterval > 0) or the batchSize is reached,
// whichever happens first.
// `bufferSize` is the size of the buffer that stores records before they are sent to the Kinesis
// stream. If the number of records in the buffer is equal to or greater than bufferSize then the
// Add method will block.
// `batchSize` is the max number of records in each batch request sent to Kinesis.
// TODO: this is too many args. Maybe instead have some defaults and add some setter methods for the less critical
// params
func New(
	client BatchingKinesisClient,
	streamName string,
	bufferSize int,
	flushInterval time.Duration,
	batchSize int,
	logger *log.Logger,
) (BatchProducer, error) {
	if batchSize < 1 || batchSize > 500 {
		return nil, errors.New("batchSize must be between 1 and 500 inclusive")
	}

	if bufferSize < batchSize && flushInterval <= 0 {
		return nil, errors.New("If bufferSize < batchSize && flushInterval <= 0 then the buffer will eventually fill up and Add will block forever.")
	}

	if flushInterval > 0 && flushInterval < 50*time.Millisecond {
		return nil, errors.New("Are you crazy?")
	}

	batchProducer := batchProducer{
		client:               client,
		streamName:           streamName,
		flushInterval:        flushInterval,
		batchSize:            batchSize,
		maxAttemptsPerRecord: 10,
		logger:               logger,
		statInterval:         time.Second,
		currentStat:          new(BatchStat),
		records:              make(chan batchRecord, bufferSize),
		stop:                 make(chan interface{}),
	}

	return &batchProducer, nil
}

type batchProducer struct {
	client               BatchingKinesisClient
	streamName           string
	flushInterval        time.Duration
	batchSize            int
	maxAttemptsPerRecord int
	logger               *log.Logger
	running              bool
	runningMu            sync.Mutex
	consecutiveErrors    int
	currentDelay         time.Duration
	statInterval         time.Duration
	statReceiver         BatchStatReceiver
	currentStat          *BatchStat
	records              chan batchRecord
	stop                 chan interface{}
}

type batchRecord struct {
	data         []byte
	partitionKey string
	sendAttempts int
}

// from/for interface BatchProducer
func (b *batchProducer) Add(data []byte, partitionKey string) error {
	if !b.isRunning() {
		return errors.New("Cannot call Add when BatchProducer is not running (to prevent the buffer filling up and Add blocking indefinitely).")
	}
	b.records <- batchRecord{data: data, partitionKey: partitionKey}
	return nil
}

// from/for interface BatchProducer
func (b *batchProducer) Start() error {
	if b.isRunning() {
		return nil
	}

	go b.run()

	for !b.isRunning() {
		time.Sleep(1 * time.Millisecond)
	}

	return nil
}

func (b *batchProducer) run() {
	flushTicker := &time.Ticker{}
	if b.flushInterval > 0 {
		flushTicker = time.NewTicker(b.flushInterval)
		defer flushTicker.Stop()
	}

	statTicker := &time.Ticker{}
	if b.statReceiver != nil && b.statInterval > 0 {
		statTicker = time.NewTicker(b.statInterval)
		defer statTicker.Stop()
	}

	b.setRunning(true)
	defer b.setRunning(false)

	for {
		select {
		case <-flushTicker.C:
			b.sendBatch()
		case <-statTicker.C:
			b.sendStats()
		case <-b.stop:
			break
		default:
			if len(b.records) >= b.batchSize {
				b.sendBatch()
			} else {
				time.Sleep(1 * time.Millisecond)
			}
		}
	}
}

// from/for interface BatchProducer
func (b *batchProducer) Stop() error {
	// TODO: Immediately stop accepting new records by Add, then block until all the records in the buffer have been sent
	if b.isRunning() {
		b.stop <- true
	}
	return nil
}

func (b *batchProducer) SetMaxAttemptsPerRecord(v int) error {
	if b.isRunning() {
		return errors.New("Cannot set max attempts per record while BatchProducer is running.")
	}
	b.maxAttemptsPerRecord = v
	return nil
}

func (b *batchProducer) SetStatReceiver(sr BatchStatReceiver) error {
	if b.isRunning() {
		return errors.New("Cannot set BatchStatReceiver while BatchProducer is running.")
	}
	b.statReceiver = sr
	return nil
}

func (b *batchProducer) SetStatInterval(si time.Duration) error {
	if b.isRunning() {
		return errors.New("Cannot set stat interval while BatchProducer is running.")
	}
	b.statInterval = si
	return nil
}

func (b *batchProducer) setRunning(running bool) {
	b.runningMu.Lock()
	defer b.runningMu.Unlock()
	b.running = running
}

func (b *batchProducer) isRunning() bool {
	b.runningMu.Lock()
	defer b.runningMu.Unlock()
	return b.running
}

func (b *batchProducer) sendBatch() {
	if len(b.records) == 0 {
		return
	}

	if b.consecutiveErrors == 1 {
		b.currentDelay = 50 * time.Millisecond
	} else if b.consecutiveErrors > 1 {
		b.currentDelay *= 2
	}

	if b.currentDelay > 0 {
		b.logger.Printf("Delaying the batch by %v because of %v consecutive errors", b.currentDelay, b.consecutiveErrors)
		time.Sleep(b.currentDelay)
	}

	records := b.takeRecordsFromBuffer()
	res, err := b.client.PutRecords(b.recordsToArgs(records))

	if err != nil {
		b.consecutiveErrors++
		b.currentStat.KinesisErrorsSinceLastStat++
		b.logger.Printf("Error occurred when sending PutRecords request to Kinesis stream %v: %v", b.streamName, err)
		b.returnRecordsToBuffer(records)
	} else {
		b.consecutiveErrors = 0
		b.currentDelay = 0
		succeeded := len(records) - res.FailedRecordCount

		b.currentStat.RecordsSentSuccessfullySinceLastStat += succeeded

		if res.FailedRecordCount > 0 {
			b.logger.Printf("Partial success when sending a PutRecords request to Kinesis stream %v: %v succeeded, %v failed. Re-enqueueing failed records.", b.streamName, succeeded, res.FailedRecordCount)
			b.returnSomeFailedRecordsToBuffer(res, records)
		}
	}
}

func (b *batchProducer) takeRecordsFromBuffer() []batchRecord {
	var size int
	bufferLen := len(b.records)
	if bufferLen >= b.batchSize {
		size = b.batchSize
	} else {
		size = bufferLen
	}

	result := make([]batchRecord, size)
	for i := 0; i < size; i++ {
		result[i] = <-b.records
	}
	return result
}

func (b *batchProducer) recordsToArgs(records []batchRecord) *kinesis.RequestArgs {
	args := kinesis.NewArgs()
	args.Add("StreamName", b.streamName)
	for _, record := range records {
		args.AddRecord(record.data, record.partitionKey)
	}
	return args
}

// TODO: perhaps we should use a deque internally as the buffer so we can return records to
// the front of the queue.
func (b *batchProducer) returnRecordsToBuffer(records []batchRecord) {
	for _, record := range records {
		// Not using b.Add because we want to preserve the value of record.sendAttempts.
		b.records <- record
	}
}

func (b *batchProducer) returnSomeFailedRecordsToBuffer(res *kinesis.PutRecordsResp, records []batchRecord) {
	for i, result := range res.Records {
		record := records[i]
		if result.ErrorCode != "" {
			record.sendAttempts++

			if record.sendAttempts < b.maxAttemptsPerRecord {
				b.logger.Printf("Re-enqueueing failed record to buffer for retry. Error code was: '%v' and message was '%v'", result.ErrorCode, result.ErrorMessage)
				// Not using b.Add because we want to preserve the value of record.sendAttempts.
				b.records <- record
			} else {
				b.currentStat.RecordsDroppedSinceLastStat++
				msg := "NOT re-enqueueing failed record for retry, as it has hit %v attempts, " +
					"which is the maximum. Error code was: '%v' and message was '%v'"
				b.logger.Printf(msg, record.sendAttempts, result.ErrorCode, result.ErrorMessage)
			}
		}
	}
}

func (b *batchProducer) sendStats() {
	b.currentStat.BufferSize = len(b.records)

	// TEMP TMEP TEMP
	// fmt.Printf("Sending stat: %+v\n", b.currentStat)

	// I considered running this as a goroutine, but I’m concerned about leaks. So instead, for now,
	// the provider of the BatchStatReceiver must ensure that it is either very fast or non-blocking.
	b.statReceiver.Receive(*b.currentStat)

	b.currentStat = new(BatchStat)
}
<|endoftext|>


<commit_before>package checker

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"mime/multipart"
	"net/http"
	"net/http/cookiejar"
	"net/textproto"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/catatsuy/private-isu/benchmarker/score"
)

const (
	UserAgent = "benchmarker"
)

var (
	targetHost string
)

type Session struct {
	Client    *http.Client
	Transport *http.Transport

	logger *log.Logger
}

func NewSession() *Session {
	w := &Session{
		logger: log.New(os.Stdout, "", 0),
	}

	jar, _ := cookiejar.New(&cookiejar.Options{})
	w.Transport = &http.Transport{}
	w.Client = &http.Client{
		Transport: w.Transport,
		Jar:       jar,
		Timeout:   time.Duration(10) * time.Second,
	}

	return w
}

func SetTargetHost(host string) (string, error) {
	parsedURL, err := url.Parse(host)

	if err != nil {
		return "", err
	}

	targetHost = ""

	// 完璧にチェックするのは難しい
	if parsedURL.Scheme == "http" {
		targetHost += parsedURL.Host
	} else if parsedURL.Scheme != "" && parsedURL.Scheme != "https" {
		targetHost += parsedURL.Scheme + ":" + parsedURL.Opaque
	} else {
		return "", fmt.Errorf("不正なホスト名です")
	}

	return targetHost, nil
}

func (s *Session) NewRequest(method, uri string, body io.Reader) (*http.Request, error) {
	parsedURL, err := url.Parse(uri)

	if err != nil {
		return nil, err
	}

	if parsedURL.Scheme == "" {
		parsedURL.Scheme = "http"
	}

	if parsedURL.Host == "" {
		parsedURL.Host = targetHost
	}

	req, err := http.NewRequest(method, parsedURL.String(), body)

	if err != nil {
		return nil, err
	}

	return req, err
}

func escapeQuotes(s string) string {
	return strings.NewReplacer("\\", "\\\\", `"`, "\\\"").Replace(s)
}

func (s *Session) NewFileUploadRequest(uri string, params map[string]string, paramName string, asset *Asset) (*http.Request, error) {
	parsedURL, err := url.Parse(uri)

	if err != nil {
		return nil, err
	}

	if parsedURL.Scheme == "" {
		parsedURL.Scheme = "http"
	}

	if parsedURL.Host == "" {
		parsedURL.Host = targetHost
	}

	file, err := os.Open(asset.Path)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)
	// part, err := writer.CreateFormFile(paramName, filepath.Base(path))
	// Content-Typeを指定できないので該当コードから実装
	h := make(textproto.MIMEHeader)
	h.Set("Content-Disposition",
		fmt.Sprintf(`form-data; name="%s"; filename="%s"`,
			escapeQuotes(paramName), escapeQuotes(filepath.Base(asset.Path))))
	h.Set("Content-Type", asset.Type)
	part, err := writer.CreatePart(h)

	if err != nil {
		return nil, err
	}
	_, err = io.Copy(part, file)

	for key, val := range params {
		_ = writer.WriteField(key, val)
	}

	err = writer.Close()
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest("POST", parsedURL.String(), body)
	if err == nil {
		req.Header.Add("Content-Type", writer.FormDataContentType())
	} else {
		return nil, err
	}

	return req, err
}

func (s *Session) RefreshClient() {
	jar, _ := cookiejar.New(&cookiejar.Options{})
	s.Transport = &http.Transport{}
	s.Client = &http.Client{
		Transport: s.Transport,
		Jar:       jar,
	}
}

func (s *Session) SendRequest(req *http.Request) (*http.Response, error) {
	req.Header.Set("User-Agent", UserAgent)

	return s.Client.Do(req)
}

func (s *Session) Success(point int64) {
	score.GetInstance().SetScore(point)
}

func (s *Session) Fail(point int64, req *http.Request, err error) error {
	score.GetInstance().SetFails(point)
	if req != nil {
		err = fmt.Errorf("%s (%s %s)", err, req.Method, req.URL.Path)
	}

	score.GetFailErrorsInstance().Append(err)
	return nil
}
<commit_msg>Failでerrを返すように<commit_after>package checker

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"mime/multipart"
	"net/http"
	"net/http/cookiejar"
	"net/textproto"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/catatsuy/private-isu/benchmarker/score"
)

const (
	UserAgent = "benchmarker"
)

var (
	targetHost string
)

type Session struct {
	Client    *http.Client
	Transport *http.Transport

	logger *log.Logger
}

func NewSession() *Session {
	w := &Session{
		logger: log.New(os.Stdout, "", 0),
	}

	jar, _ := cookiejar.New(&cookiejar.Options{})
	w.Transport = &http.Transport{}
	w.Client = &http.Client{
		Transport: w.Transport,
		Jar:       jar,
		Timeout:   time.Duration(10) * time.Second,
	}

	return w
}

func SetTargetHost(host string) (string, error) {
	parsedURL, err := url.Parse(host)

	if err != nil {
		return "", err
	}

	targetHost = ""

	// 完璧にチェックするのは難しい
	if parsedURL.Scheme == "http" {
		targetHost += parsedURL.Host
	} else if parsedURL.Scheme != "" && parsedURL.Scheme != "https" {
		targetHost += parsedURL.Scheme + ":" + parsedURL.Opaque
	} else {
		return "", fmt.Errorf("不正なホスト名です")
	}

	return targetHost, nil
}

func (s *Session) NewRequest(method, uri string, body io.Reader) (*http.Request, error) {
	parsedURL, err := url.Parse(uri)

	if err != nil {
		return nil, err
	}

	if parsedURL.Scheme == "" {
		parsedURL.Scheme = "http"
	}

	if parsedURL.Host == "" {
		parsedURL.Host = targetHost
	}

	req, err := http.NewRequest(method, parsedURL.String(), body)

	if err != nil {
		return nil, err
	}

	return req, err
}

func escapeQuotes(s string) string {
	return strings.NewReplacer("\\", "\\\\", `"`, "\\\"").Replace(s)
}

func (s *Session) NewFileUploadRequest(uri string, params map[string]string, paramName string, asset *Asset) (*http.Request, error) {
	parsedURL, err := url.Parse(uri)

	if err != nil {
		return nil, err
	}

	if parsedURL.Scheme == "" {
		parsedURL.Scheme = "http"
	}

	if parsedURL.Host == "" {
		parsedURL.Host = targetHost
	}

	file, err := os.Open(asset.Path)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)
	// part, err := writer.CreateFormFile(paramName, filepath.Base(path))
	// Content-Typeを指定できないので該当コードから実装
	h := make(textproto.MIMEHeader)
	h.Set("Content-Disposition",
		fmt.Sprintf(`form-data; name="%s"; filename="%s"`,
			escapeQuotes(paramName), escapeQuotes(filepath.Base(asset.Path))))
	h.Set("Content-Type", asset.Type)
	part, err := writer.CreatePart(h)

	if err != nil {
		return nil, err
	}
	_, err = io.Copy(part, file)

	for key, val := range params {
		_ = writer.WriteField(key, val)
	}

	err = writer.Close()
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest("POST", parsedURL.String(), body)
	if err == nil {
		req.Header.Add("Content-Type", writer.FormDataContentType())
	} else {
		return nil, err
	}

	return req, err
}

func (s *Session) RefreshClient() {
	jar, _ := cookiejar.New(&cookiejar.Options{})
	s.Transport = &http.Transport{}
	s.Client = &http.Client{
		Transport: s.Transport,
		Jar:       jar,
	}
}

func (s *Session) SendRequest(req *http.Request) (*http.Response, error) {
	req.Header.Set("User-Agent", UserAgent)

	return s.Client.Do(req)
}

func (s *Session) Success(point int64) {
	score.GetInstance().SetScore(point)
}

func (s *Session) Fail(point int64, req *http.Request, err error) error {
	score.GetInstance().SetFails(point)
	if req != nil {
		err = fmt.Errorf("%s (%s %s)", err, req.Method, req.URL.Path)
	}

	score.GetFailErrorsInstance().Append(err)
	return err
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package indexers

import (
	"github.com/btcsuite/btcd/blockchain"
	"github.com/btcsuite/btcd/chaincfg/chainhash"
	"github.com/btcsuite/btcd/database"
	"github.com/btcsuite/btcutil"
	"github.com/btcsuite/btcutil/gcs/builder"
)

const (
	// cfIndexName is the human-readable name for the index.
	cfIndexName = "committed filter index"
)

// Committed filters come in two flavours: basic and extended. They are
// generated and dropped in pairs, and both are indexed by a block's hash.
// Besides holding different content, they also live in different buckets.
var (
	// cfBasicIndexKey is the name of the db bucket used to house the
	// block hash -> Basic cf index (cf#0).
	cfBasicIndexKey = []byte("cf0byhashidx")
	// cfExtendedIndexKey is the name of the db bucket used to house the
	// block hash -> Extended cf index (cf#1).
	cfExtendedIndexKey = []byte("cf1byhashidx")
)

// dbFetchBasicEntry() retrieves a block's basic filter. An entry's absence is
// not considered an error. The filter is returned serialized.
func dbFetchBasicEntry(dbTx database.Tx, h *chainhash.Hash) ([]byte, error) {
	idx := dbTx.Metadata().Bucket(cfBasicIndexKey)
	return idx.Get(h[:]), nil
}

// dbFetchExtendedEntry() retrieves a block's extended filter. An entry's
// absence is not considered an error. The filter is returned serialized.
func dbFetchExtendedEntry(dbTx database.Tx, h *chainhash.Hash) ([]byte, error) {
	idx := dbTx.Metadata().Bucket(cfExtendedIndexKey)
	return idx.Get(h[:]), nil
}

// dbStoreBasicEntry() stores a block's basic filter.
func dbStoreBasicEntry(dbTx database.Tx, h *chainhash.Hash, f []byte) error {
	idx := dbTx.Metadata().Bucket(cfBasicIndexKey)
	return idx.Put(h[:], f)
}

// dbStoreBasicEntry() stores a block's extended filter.
func dbStoreExtendedEntry(dbTx database.Tx, h *chainhash.Hash, f []byte) error {
	idx := dbTx.Metadata().Bucket(cfExtendedIndexKey)
	return idx.Put(h[:], f)
}

// dbDeleteBasicEntry() deletes a block's basic filter.
func dbDeleteBasicEntry(dbTx database.Tx, h *chainhash.Hash) error {
	idx := dbTx.Metadata().Bucket(cfBasicIndexKey)
	return idx.Delete(h[:])
}

// dbDeleteExtendedEntry() deletes a block's extended filter.
func dbDeleteExtendedEntry(dbTx database.Tx, h *chainhash.Hash) error {
	idx := dbTx.Metadata().Bucket(cfExtendedIndexKey)
	return idx.Delete(h[:])
}

// CfIndex implements a committed filter (cf) by hash index.
type CfIndex struct {
	db database.DB
}

// Ensure the CfIndex type implements the Indexer interface.
var _ Indexer = (*CfIndex)(nil)

// Init initializes the hash-based cf index. This is part of the Indexer
// interface.
func (idx *CfIndex) Init() error {
	return nil // Nothing to do.
}

// Key returns the database key to use for the index as a byte slice. This is
// part of the Indexer interface.
func (idx *CfIndex) Key() []byte {
	return cfBasicIndexKey
}

// Name returns the human-readable name of the index. This is part of the
// Indexer interface.
func (idx *CfIndex) Name() string {
	return cfIndexName
}

// Create is invoked when the indexer manager determines the index needs to be
// created for the first time. It creates buckets for the two hash-based cf
// indexes (simple, extended).
func (idx *CfIndex) Create(dbTx database.Tx) error {
	meta := dbTx.Metadata()
	_, err := meta.CreateBucket(cfBasicIndexKey)
	if err != nil {
		return err
	}
	_, err = meta.CreateBucket(cfExtendedIndexKey)
	return err
}

// makeBasicFilter() builds a block's basic filter, which consists of all
// outpoints and pkscript data pushes referenced by transactions within the
// block.
func makeBasicFilterForBlock(block *btcutil.Block) ([]byte, error) {
	b := builder.WithKeyHash(block.Hash())
	_, err := b.Key()
	if err != nil {
		return nil, err
	}
	for _, tx := range block.Transactions() {
		for _, txIn := range tx.MsgTx().TxIn {
			b.AddOutPoint(txIn.PreviousOutPoint)
		}
		for _, txOut := range tx.MsgTx().TxOut {
			b.AddScript(txOut.PkScript)
		}
	}
	f, err := b.Build()
	if err != nil {
		return nil, err
	}
	return f.Bytes(), nil
}

// makeExtendedFilter() builds a block's extended filter, which consists of
// all tx hashes and sigscript data pushes contained in the block.
func makeExtendedFilterForBlock(block *btcutil.Block) ([]byte, error) {
	b := builder.WithKeyHash(block.Hash())
	_, err := b.Key()
	if err != nil {
		return nil, err
	}
	for _, tx := range block.Transactions() {
		b.AddHash(tx.Hash())
		for _, txIn := range tx.MsgTx().TxIn {
			b.AddScript(txIn.SignatureScript)
		}
	}
	f, err := b.Build()
	if err != nil {
		return nil, err
	}
	return f.Bytes(), nil
}

// ConnectBlock is invoked by the index manager when a new block has been
// connected to the main chain. This indexer adds a hash-to-cf mapping for
// every passed block. This is part of the Indexer interface.
func (idx *CfIndex) ConnectBlock(dbTx database.Tx, block *btcutil.Block,
    view *blockchain.UtxoViewpoint) error {
	f, err := makeBasicFilterForBlock(block)
	if err != nil {
		return err
	}
	err = dbStoreBasicEntry(dbTx, block.Hash(), f)
	if err != nil {
		return err
	}
	f, err = makeExtendedFilterForBlock(block)
	if err != nil {
		return err
	}
	return dbStoreExtendedEntry(dbTx, block.Hash(), f)
}

// DisconnectBlock is invoked by the index manager when a block has been
// disconnected from the main chain.  This indexer removes the hash-to-cf
// mapping for every passed block. This is part of the Indexer interface.
func (idx *CfIndex) DisconnectBlock(dbTx database.Tx, block *btcutil.Block,
    view *blockchain.UtxoViewpoint) error {
	err := dbDeleteBasicEntry(dbTx, block.Hash())
	if err != nil {
		return err
	}
	return dbDeleteExtendedEntry(dbTx, block.Hash())
}

func (idx *CfIndex) FilterByBlockHash(hash *chainhash.Hash, extended bool) ([]byte, error) {
	var filterBytes []byte
	err := idx.db.View(func(dbTx database.Tx) error {
		var err error
		if extended {
			filterBytes, err = dbFetchExtendedEntry(dbTx, hash)
		} else {
			filterBytes, err = dbFetchBasicEntry(dbTx, hash)
		}
		return err
	})
	return filterBytes, err
}

// NewCfIndex returns a new instance of an indexer that is used to create a
// mapping of the hashes of all blocks in the blockchain to their respective
// committed filters.
//
// It implements the Indexer interface which plugs into the IndexManager that in
// turn is used by the blockchain package. This allows the index to be
// seamlessly maintained along with the chain.
func NewCfIndex(db database.DB) *CfIndex {
	return &CfIndex{db: db}
}

// DropCfIndex drops the CF index from the provided database if exists.
func DropCfIndex(db database.DB) error {
	return dropIndex(db, cfBasicIndexKey, cfIndexName)
}
<commit_msg>Create db buckets for committed filter hashes<commit_after>// Copyright (c) 2017 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package indexers

import (
	"github.com/btcsuite/btcd/blockchain"
	"github.com/btcsuite/btcd/chaincfg/chainhash"
	"github.com/btcsuite/btcd/database"
	"github.com/btcsuite/btcutil"
	"github.com/btcsuite/btcutil/gcs/builder"
)

const (
	// cfIndexName is the human-readable name for the index.
	cfIndexName = "committed filter index"
)

// Committed filters come in two flavours: basic and extended. They are
// generated and dropped in pairs, and both are indexed by a block's hash.
// Besides holding different content, they also live in different buckets.
var (
	// cfBasicIndexKey is the name of the db bucket used to house the
	// block hash -> basic cf index (cf#0).
	cfBasicIndexKey = []byte("cf0byhashidx")
	// cfBasicHeaderKey is the name of the db bucket used to house the
	// block hash -> basic cf header index (cf#0).
	cfBasicHeaderKey = []byte("cf0headerbyhashidx")
	// cfExtendedIndexKey is the name of the db bucket used to house the
	// block hash -> extended cf index (cf#1).
	cfExtendedIndexKey = []byte("cf1byhashidx")
	// cfExtendedHeaderKey is the name of the db bucket used to house the
	// block hash -> extended cf header index (cf#1).
	cfExtendedHeaderKey = []byte("cf1headerbyhashidx")
)

// dbFetchBasicEntry() retrieves a block's basic filter. An entry's absence is
// not considered an error. The filter is returned serialized.
func dbFetchBasicEntry(dbTx database.Tx, h *chainhash.Hash) ([]byte, error) {
	idx := dbTx.Metadata().Bucket(cfBasicIndexKey)
	return idx.Get(h[:]), nil
}

// dbFetchExtendedEntry() retrieves a block's extended filter. An entry's
// absence is not considered an error. The filter is returned serialized.
func dbFetchExtendedEntry(dbTx database.Tx, h *chainhash.Hash) ([]byte, error) {
	idx := dbTx.Metadata().Bucket(cfExtendedIndexKey)
	return idx.Get(h[:]), nil
}

// dbStoreBasicEntry() stores a block's basic filter.
func dbStoreBasicEntry(dbTx database.Tx, h *chainhash.Hash, f []byte) error {
	idx := dbTx.Metadata().Bucket(cfBasicIndexKey)
	return idx.Put(h[:], f)
}

// dbStoreBasicEntry() stores a block's extended filter.
func dbStoreExtendedEntry(dbTx database.Tx, h *chainhash.Hash, f []byte) error {
	idx := dbTx.Metadata().Bucket(cfExtendedIndexKey)
	return idx.Put(h[:], f)
}

// dbDeleteBasicEntry() deletes a block's basic filter.
func dbDeleteBasicEntry(dbTx database.Tx, h *chainhash.Hash) error {
	idx := dbTx.Metadata().Bucket(cfBasicIndexKey)
	return idx.Delete(h[:])
}

// dbDeleteExtendedEntry() deletes a block's extended filter.
func dbDeleteExtendedEntry(dbTx database.Tx, h *chainhash.Hash) error {
	idx := dbTx.Metadata().Bucket(cfExtendedIndexKey)
	return idx.Delete(h[:])
}

// CfIndex implements a committed filter (cf) by hash index.
type CfIndex struct {
	db database.DB
}

// Ensure the CfIndex type implements the Indexer interface.
var _ Indexer = (*CfIndex)(nil)

// Init initializes the hash-based cf index. This is part of the Indexer
// interface.
func (idx *CfIndex) Init() error {
	return nil // Nothing to do.
}

// Key returns the database key to use for the index as a byte slice. This is
// part of the Indexer interface.
func (idx *CfIndex) Key() []byte {
	return cfBasicIndexKey
}

// Name returns the human-readable name of the index. This is part of the
// Indexer interface.
func (idx *CfIndex) Name() string {
	return cfIndexName
}

// Create is invoked when the indexer manager determines the index needs to be
// created for the first time. It creates buckets for the two hash-based cf
// indexes (simple, extended).
func (idx *CfIndex) Create(dbTx database.Tx) error {
	meta := dbTx.Metadata()
	_, err := meta.CreateBucket(cfBasicIndexKey)
	if err != nil {
		return err
	}
	_, err = meta.CreateBucket(cfBasicHeaderKey)
	if err != nil {
		return err
	}
	_, err = meta.CreateBucket(cfExtendedIndexKey)
	if err != nil {
		return err
	}
	_, err = meta.CreateBucket(cfExtendedHeaderKey)
	return err
}

// makeBasicFilter() builds a block's basic filter, which consists of all
// outpoints and pkscript data pushes referenced by transactions within the
// block.
func makeBasicFilterForBlock(block *btcutil.Block) ([]byte, error) {
	b := builder.WithKeyHash(block.Hash())
	_, err := b.Key()
	if err != nil {
		return nil, err
	}
	for _, tx := range block.Transactions() {
		for _, txIn := range tx.MsgTx().TxIn {
			b.AddOutPoint(txIn.PreviousOutPoint)
		}
		for _, txOut := range tx.MsgTx().TxOut {
			b.AddScript(txOut.PkScript)
		}
	}
	f, err := b.Build()
	if err != nil {
		return nil, err
	}
	return f.Bytes(), nil
}

// makeExtendedFilter() builds a block's extended filter, which consists of
// all tx hashes and sigscript data pushes contained in the block.
func makeExtendedFilterForBlock(block *btcutil.Block) ([]byte, error) {
	b := builder.WithKeyHash(block.Hash())
	_, err := b.Key()
	if err != nil {
		return nil, err
	}
	for _, tx := range block.Transactions() {
		b.AddHash(tx.Hash())
		for _, txIn := range tx.MsgTx().TxIn {
			b.AddScript(txIn.SignatureScript)
		}
	}
	f, err := b.Build()
	if err != nil {
		return nil, err
	}
	return f.Bytes(), nil
}

// ConnectBlock is invoked by the index manager when a new block has been
// connected to the main chain. This indexer adds a hash-to-cf mapping for
// every passed block. This is part of the Indexer interface.
func (idx *CfIndex) ConnectBlock(dbTx database.Tx, block *btcutil.Block,
    view *blockchain.UtxoViewpoint) error {
	f, err := makeBasicFilterForBlock(block)
	if err != nil {
		return err
	}
	err = dbStoreBasicEntry(dbTx, block.Hash(), f)
	if err != nil {
		return err
	}
	f, err = makeExtendedFilterForBlock(block)
	if err != nil {
		return err
	}
	return dbStoreExtendedEntry(dbTx, block.Hash(), f)
}

// DisconnectBlock is invoked by the index manager when a block has been
// disconnected from the main chain.  This indexer removes the hash-to-cf
// mapping for every passed block. This is part of the Indexer interface.
func (idx *CfIndex) DisconnectBlock(dbTx database.Tx, block *btcutil.Block,
    view *blockchain.UtxoViewpoint) error {
	err := dbDeleteBasicEntry(dbTx, block.Hash())
	if err != nil {
		return err
	}
	return dbDeleteExtendedEntry(dbTx, block.Hash())
}

func (idx *CfIndex) FilterByBlockHash(hash *chainhash.Hash, extended bool) ([]byte, error) {
	var filterBytes []byte
	err := idx.db.View(func(dbTx database.Tx) error {
		var err error
		if extended {
			filterBytes, err = dbFetchExtendedEntry(dbTx, hash)
		} else {
			filterBytes, err = dbFetchBasicEntry(dbTx, hash)
		}
		return err
	})
	return filterBytes, err
}

// NewCfIndex returns a new instance of an indexer that is used to create a
// mapping of the hashes of all blocks in the blockchain to their respective
// committed filters.
//
// It implements the Indexer interface which plugs into the IndexManager that in
// turn is used by the blockchain package. This allows the index to be
// seamlessly maintained along with the chain.
func NewCfIndex(db database.DB) *CfIndex {
	return &CfIndex{db: db}
}

// DropCfIndex drops the CF index from the provided database if exists.
func DropCfIndex(db database.DB) error {
	return dropIndex(db, cfBasicIndexKey, cfIndexName)
}
<|endoftext|>


<commit_before>// render.go embodies rendering codecs for different message patterns eg json, html templates

package relay

import (
	"bytes"
	"encoding/json"
	"encoding/xml"
	"errors"
	"mime/multipart"
	"net/url"
	"text/template"
)

const (
	// ContentBinary header value for binary data.
	ContentBinary = "application/octet-stream"
	// ContentHTML header value for HTML data.
	ContentHTML = "text/html"
	// ContentJSON header value for JSON data.
	ContentJSON = "application/json"
	// ContentJSONP header value for JSONP data.
	ContentJSONP = "application/javascript"
	// ContentLength header constant.
	ContentLength = "Content-Length"
	// ContentText header value for Text data.
	ContentText = "text/plain"
	// ContentType header constant.
	ContentType = "Content-Type"
	// ContentXHTML header value for XHTML data.
	ContentXHTML = "application/xhtml+xml"
	// ContentXML header value for XML data.
	ContentXML = "text/xml"
	// Default character encoding.
	defaultCharset = "charset=UTF-8"
)

// Message represent a message data
type Message struct {
	MessageType string
	Method      string
	Payload     []byte
	Form        url.Values
	PostForm    url.Values
	Multipart   *multipart.Form
	Params      Collector
}

//MessageDecoder provides the message decoding decoder for *HTTPRequest objects
var MessageDecoder = NewHTTPDecoder(func(req *HTTPRequest) (*Message, error) {
	return loadData(req)
})

// UseHTTPEncoder wires up the MessageDecoder as an automatic decoder
func UseHTTPEncoder(enc HTTPEncoder) HTTPCodec {
	return NewHTTPCodec(enc, MessageDecoder)
}

//SimpleEncoder provides simple encoding that checks if the value given is a string or []byte else returns an error
var SimpleEncoder = NewHTTPEncoder(func(req *HTTPRequest, d interface{}) (int, error) {

	switch d.(type) {
	case string:
		so, _ := d.(string)
		return req.Res.Write([]byte(so))
	case []byte:
		bo, _ := d.([]byte)
		return req.Res.Write(bo)
	}

	return 0, NewCustomError("SimpleEncoder", "data is neither a 'string' or '[]byte' type ")
})

// BasicHTTPCodec returns a new codec based on the basic deocder and encoder
var BasicHTTPCodec = NewHTTPCodec(SimpleEncoder, MessageDecoder)

// Head provides a basic message status and head values
type Head struct {
	Status  int
	Content string
}

// Text provides a basic text messages
type Text struct {
	Head
	Data string
}

//TextEncoder provides the jsonp encoder for encoding json messages
var TextEncoder = NewHTTPEncoder(func(r *HTTPRequest, d interface{}) (int, error) {
	setUpHeadings(r)

	tx, ok := d.(Text)

	if !ok {
		return 0, NewCustomError("TextEncoder", "received type is not a Text{}")
	}

	r.Res.WriteHeader(tx.Status)

	if tx.Content == "" {
		r.Res.Header().Add("Content-Type", ContentText)
	} else {
		r.Res.Header().Add("Content-Type", tx.Content)
	}

	return r.Res.Write([]byte(tx.Data))
})

// TextRender returns a text struct for rendering
func TextRender(status int, data string) *Text {
	return &Text{
		Data: data,
		Head: Head{
			Status:  status,
			Content: ContentText,
		},
	}
}

// JSONP provides a basic jsonp messages
type JSONP struct {
	Head
	Indent   bool
	Callback string
	Data     interface{}
}

//JSONPEncoder provides the jsonp encoder for encoding json messages
var JSONPEncoder = NewHTTPEncoder(func(req *HTTPRequest, d interface{}) (int, error) {

	var res []byte
	var err error

	jop, ok := d.(JSONP)

	if !ok {
		return 0, NewCustomError("JSONP", "encoder expected JSONP type")
	}

	if jop.Indent {
		res, err = json.MarshalIndent(jop.Data, "", "  ")
		res = append(res, '\n')
	} else {
		res, err = json.Marshal(jop.Data)
	}

	if err != nil {
		return 0, err
	}

	if jop.Content == "" {
		req.Res.Header().Add("Content-Type", ContentJSONP)
	} else {
		req.Res.Header().Add("Content-Type", jop.Content)
	}
	// req.Res.Header().Add("Content-Type", jop.Content)
	req.Res.WriteHeader(jop.Status)

	var fos []byte
	fos = append(fos, []byte(jop.Callback+"(")...)
	fos = append(fos, res...)
	fos = append(fos, []byte(");")...)

	return req.Res.Write(fos)
})

// JSONPRender returns a jsonp struct for rendering
func JSONPRender(status int, indent bool, callback string, data interface{}) *JSONP {
	return &JSONP{
		Indent:   indent,
		Callback: callback,
		Data:     data,
		Head: Head{
			Status:  status,
			Content: ContentJSONP,
		},
	}
}

// JSON provides a basic json messages
type JSON struct {
	Head
	Indent       bool
	UnEscapeHTML bool
	Stream       bool
	Data         interface{}
}

//JSONEncoder provides the jsonp encoder for encoding json messages
var JSONEncoder = NewHTTPEncoder(func(req *HTTPRequest, d interface{}) (int, error) {

	jso, ok := d.(JSON)

	if !ok {
		return 0, NewCustomError("JSON", "Wrong type,expected JSON type")
	}

	if jso.Content == "" {
		req.Res.Header().Add("Content-Type", ContentJSON)
	} else {
		req.Res.Header().Add("Content-Type", jso.Content)
	}

	// req.Res.Header().Add("Content-Type", ContentJSON)
	if jso.Stream {
		// req.Res.Header().Add("Content-Type", jso.Content)
		req.Res.WriteHeader(jso.Status)
		return 1, json.NewEncoder(req.Res).Encode(jso.Data)
	}

	var res []byte
	var err error

	if jso.Indent {
		res, err = json.MarshalIndent(jso.Data, "", "  ")
		res = append(res, '\n')
	} else {
		res, err = json.Marshal(jso.Data)
	}

	if err != nil {
		return 0, err
	}

	// Unescape HTML if needed.
	if jso.UnEscapeHTML {
		res = bytes.Replace(res, []byte("\\u003c"), []byte("<"), -1)
		res = bytes.Replace(res, []byte("\\u003e"), []byte(">"), -1)
		res = bytes.Replace(res, []byte("\\u0026"), []byte("&"), -1)
	}

	req.Res.Header().Add("Content-Type", jso.Content)
	req.Res.WriteHeader(jso.Status)
	req.Res.Write(res)

	return 0, nil
})

// JSONRender returns a json struct for rendering
func JSONRender(status int, data interface{}, indent, stream, unescape bool) *JSON {
	return &JSON{
		Indent:       indent,
		Stream:       stream,
		UnEscapeHTML: unescape,
		Data:         data,
		Head: Head{
			Status:  status,
			Content: ContentJSON,
		},
	}
}

// HTML provides a basic html messages
type HTML struct {
	Head
	Name     string
	Layout   string
	Binding  interface{}
	Template *template.Template
}

//HTMLEncoder provides the jsonp encoder for encoding json messages
var HTMLEncoder = NewHTTPEncoder(func(req *HTTPRequest, d interface{}) (int, error) {

	hop, ok := d.(HTML)

	if !ok {
		return 0, NewCustomError("HTML", "encoder received wrong type,expected HTML struct type")
	}

	bou := bufPool.Get()

	if err := hop.Template.ExecuteTemplate(bou, hop.Name, hop.Binding); err != nil {
		return 0, err
	}

	if hop.Content == "" {
		req.Res.Header().Add("Content-Type", ContentHTML)
	} else {
		req.Res.Header().Add("Content-Type", hop.Content)
	}

	nd, err := bou.WriteTo(req.Res)

	bufPool.Put(bou)

	return int(nd), err
})

// HTMLRender returns a html struct for rendering
func HTMLRender(status int, name, layout string, binding interface{}, tl *template.Template) *HTML {
	return &HTML{
		Name:     name,
		Layout:   layout,
		Binding:  binding,
		Template: tl,
		Head: Head{
			Status:  status,
			Content: ContentHTML,
		},
	}
}

// XML provides a basic html messages
type XML struct {
	Head
	Indent bool
	Prefix []byte
	Data   interface{}
}

//XMLEncoder provides the jsonp encoder for encoding json messages
var XMLEncoder = NewHTTPEncoder(func(req *HTTPRequest, d interface{}) (int, error) {

	jso, ok := d.(XML)

	if !ok {
		return 0, NewCustomError("XML", "Wrong type,expected XML type")
	}

	if jso.Content == "" {
		req.Res.Header().Add("Content-Type", ContentXML)
	} else {
		req.Res.Header().Add("Content-Type", jso.Content)
	}

	var res []byte
	var err error

	if jso.Indent {
		res, err = xml.MarshalIndent(jso.Data, "", "  ")
		res = append(res, '\n')
	} else {
		res, err = xml.Marshal(jso.Data)
	}

	if err != nil {
		return 0, err
	}

	return req.Res.Write(res)
})

// XMLRender returns a html struct for rendering
func XMLRender(status int, indent bool, data interface{}, prefix []byte) *XML {
	return &XML{
		Indent: indent,
		Prefix: prefix,
		Data:   data,
		Head: Head{
			Status:  status,
			Content: ContentXML,
		},
	}
}

// ErrInvalidByteType is returned when the interface is ot a []byte
var ErrInvalidByteType = errors.New("interface not a []byte")

//ByteSocketEncoder provides the basic websocket message encoder for encoding json messages
var ByteSocketEncoder = NewSocketEncoder(func(w *Websocket, t int, bu interface{}) (int, error) {
	var err error
	var size int

	bo, ok := bu.([]byte)

	if !ok {
		return 0, ErrInvalidByteType
	}

	err = w.Conn.WriteMessage(t, bo)

	if err == nil {
		size = len(bo)
	}

	return size, err
})

//ByteSocketDecoder provides the basic websocket decoder which justs returns a decoder
var ByteSocketDecoder = NewSocketDecoder(func(t int, bu []byte) (interface{}, error) {
	return bu, nil
})

// BasicSocketCodec returns a codec using the socket encoder and decoder
var BasicSocketCodec = NewSocketCodec(ByteSocketEncoder, ByteSocketDecoder)
<commit_msg>adding html encoder<commit_after>// render.go embodies rendering codecs for different message patterns eg json, html templates

package relay

import (
	"bytes"
	"encoding/json"
	"encoding/xml"
	"errors"
	"mime/multipart"
	"net/url"
	"text/template"
)

const (
	// ContentBinary header value for binary data.
	ContentBinary = "application/octet-stream"
	// ContentHTML header value for HTML data.
	ContentHTML = "text/html"
	// ContentJSON header value for JSON data.
	ContentJSON = "application/json"
	// ContentJSONP header value for JSONP data.
	ContentJSONP = "application/javascript"
	// ContentLength header constant.
	ContentLength = "Content-Length"
	// ContentText header value for Text data.
	ContentText = "text/plain"
	// ContentType header constant.
	ContentType = "Content-Type"
	// ContentXHTML header value for XHTML data.
	ContentXHTML = "application/xhtml+xml"
	// ContentXML header value for XML data.
	ContentXML = "text/xml"
	// Default character encoding.
	defaultCharset = "charset=UTF-8"
)

// Message represent a message data
type Message struct {
	MessageType string
	Method      string
	Payload     []byte
	Form        url.Values
	PostForm    url.Values
	Multipart   *multipart.Form
	Params      Collector
}

//MessageDecoder provides the message decoding decoder for *HTTPRequest objects
var MessageDecoder = NewHTTPDecoder(func(req *HTTPRequest) (*Message, error) {
	return loadData(req)
})

// UseHTTPEncoder wires up the MessageDecoder as an automatic decoder
func UseHTTPEncoder(enc HTTPEncoder) HTTPCodec {
	return NewHTTPCodec(enc, MessageDecoder)
}

//SimpleEncoder provides simple encoding that checks if the value given is a string or []byte else returns an error
var SimpleEncoder = NewHTTPEncoder(func(req *HTTPRequest, d interface{}) (int, error) {

	switch d.(type) {
	case string:
		so, _ := d.(string)
		return req.Res.Write([]byte(so))
	case []byte:
		bo, _ := d.([]byte)
		return req.Res.Write(bo)
	}

	return 0, NewCustomError("SimpleEncoder", "data is neither a 'string' or '[]byte' type ")
})

// BasicHTTPCodec returns a new codec based on the basic deocder and encoder
var BasicHTTPCodec = NewHTTPCodec(SimpleEncoder, MessageDecoder)

// Head provides a basic message status and head values
type Head struct {
	Status  int
	Content string
}

// Text provides a basic text messages
type Text struct {
	Head
	Data string
}

//TextEncoder provides the jsonp encoder for encoding json messages
var TextEncoder = NewHTTPEncoder(func(r *HTTPRequest, d interface{}) (int, error) {
	setUpHeadings(r)

	tx, ok := d.(Text)

	if !ok {
		return 0, NewCustomError("TextEncoder", "received type is not a Text{}")
	}

	r.Res.WriteHeader(tx.Status)

	if tx.Content == "" {
		r.Res.Header().Add("Content-Type", ContentText)
	} else {
		r.Res.Header().Add("Content-Type", tx.Content)
	}

	return r.Res.Write([]byte(tx.Data))
})

// TextRender returns a text struct for rendering
func TextRender(status int, data string) *Text {
	return &Text{
		Data: data,
		Head: Head{
			Status:  status,
			Content: ContentText,
		},
	}
}

// JSONP provides a basic jsonp messages
type JSONP struct {
	Head
	Indent   bool
	Callback string
	Data     interface{}
}

//JSONPEncoder provides the jsonp encoder for encoding json messages
var JSONPEncoder = NewHTTPEncoder(func(req *HTTPRequest, d interface{}) (int, error) {

	var res []byte
	var err error

	jop, ok := d.(JSONP)

	if !ok {
		return 0, NewCustomError("JSONP", "encoder expected JSONP type")
	}

	if jop.Indent {
		res, err = json.MarshalIndent(jop.Data, "", "  ")
		res = append(res, '\n')
	} else {
		res, err = json.Marshal(jop.Data)
	}

	if err != nil {
		return 0, err
	}

	if jop.Content == "" {
		req.Res.Header().Add("Content-Type", ContentJSONP)
	} else {
		req.Res.Header().Add("Content-Type", jop.Content)
	}
	// req.Res.Header().Add("Content-Type", jop.Content)
	req.Res.WriteHeader(jop.Status)

	var fos []byte
	fos = append(fos, []byte(jop.Callback+"(")...)
	fos = append(fos, res...)
	fos = append(fos, []byte(");")...)

	return req.Res.Write(fos)
})

// JSONPRender returns a jsonp struct for rendering
func JSONPRender(status int, indent bool, callback string, data interface{}) *JSONP {
	return &JSONP{
		Indent:   indent,
		Callback: callback,
		Data:     data,
		Head: Head{
			Status:  status,
			Content: ContentJSONP,
		},
	}
}

// JSON provides a basic json messages
type JSON struct {
	Head
	Indent       bool
	UnEscapeHTML bool
	Stream       bool
	Data         interface{}
}

//JSONEncoder provides the jsonp encoder for encoding json messages
var JSONEncoder = NewHTTPEncoder(func(req *HTTPRequest, d interface{}) (int, error) {

	jso, ok := d.(JSON)

	if !ok {
		return 0, NewCustomError("JSON", "Wrong type,expected JSON type")
	}

	if jso.Content == "" {
		req.Res.Header().Add("Content-Type", ContentJSON)
	} else {
		req.Res.Header().Add("Content-Type", jso.Content)
	}

	// req.Res.Header().Add("Content-Type", ContentJSON)
	if jso.Stream {
		// req.Res.Header().Add("Content-Type", jso.Content)
		req.Res.WriteHeader(jso.Status)
		return 1, json.NewEncoder(req.Res).Encode(jso.Data)
	}

	var res []byte
	var err error

	if jso.Indent {
		res, err = json.MarshalIndent(jso.Data, "", "  ")
		res = append(res, '\n')
	} else {
		res, err = json.Marshal(jso.Data)
	}

	if err != nil {
		return 0, err
	}

	// Unescape HTML if needed.
	if jso.UnEscapeHTML {
		res = bytes.Replace(res, []byte("\\u003c"), []byte("<"), -1)
		res = bytes.Replace(res, []byte("\\u003e"), []byte(">"), -1)
		res = bytes.Replace(res, []byte("\\u0026"), []byte("&"), -1)
	}

	req.Res.Header().Add("Content-Type", jso.Content)
	req.Res.WriteHeader(jso.Status)
	req.Res.Write(res)

	return 0, nil
})

// JSONRender returns a json struct for rendering
func JSONRender(status int, data interface{}, indent, stream, unescape bool) *JSON {
	return &JSON{
		Indent:       indent,
		Stream:       stream,
		UnEscapeHTML: unescape,
		Data:         data,
		Head: Head{
			Status:  status,
			Content: ContentJSON,
		},
	}
}

// HTML provides a basic html messages
type HTML struct {
	Head
	// Name     string
	Layout   string
	Binding  interface{}
	Template *template.Template
}

//HTMLEncoder provides the jsonp encoder for encoding json messages
var HTMLEncoder = NewHTTPEncoder(func(req *HTTPRequest, d interface{}) (int, error) {

	hop, ok := d.(HTML)

	if !ok {
		return 0, NewCustomError("HTML", "encoder received wrong type,expected HTML struct type")
	}

	bou := bufPool.Get()

	if err := hop.Template.ExecuteTemplate(bou, hop.Layout, hop.Binding); err != nil {
		return 0, err
	}

	if hop.Content == "" {
		req.Res.Header().Add("Content-Type", ContentHTML)
	} else {
		req.Res.Header().Add("Content-Type", hop.Content)
	}

	nd, err := bou.WriteTo(req.Res)

	bufPool.Put(bou)

	return int(nd), err
})

// HTMLRender returns a html struct for rendering
func HTMLRender(status int, layout string, binding interface{}, tl *template.Template) *HTML {
	return &HTML{
		// Name:     name,
		Layout:   layout,
		Binding:  binding,
		Template: tl,
		Head: Head{
			Status:  status,
			Content: ContentHTML,
		},
	}
}

// XML provides a basic html messages
type XML struct {
	Head
	Indent bool
	Prefix []byte
	Data   interface{}
}

//XMLEncoder provides the jsonp encoder for encoding json messages
var XMLEncoder = NewHTTPEncoder(func(req *HTTPRequest, d interface{}) (int, error) {

	jso, ok := d.(XML)

	if !ok {
		return 0, NewCustomError("XML", "Wrong type,expected XML type")
	}

	if jso.Content == "" {
		req.Res.Header().Add("Content-Type", ContentXML)
	} else {
		req.Res.Header().Add("Content-Type", jso.Content)
	}

	var res []byte
	var err error

	if jso.Indent {
		res, err = xml.MarshalIndent(jso.Data, "", "  ")
		res = append(res, '\n')
	} else {
		res, err = xml.Marshal(jso.Data)
	}

	if err != nil {
		return 0, err
	}

	return req.Res.Write(res)
})

// XMLRender returns a html struct for rendering
func XMLRender(status int, indent bool, data interface{}, prefix []byte) *XML {
	return &XML{
		Indent: indent,
		Prefix: prefix,
		Data:   data,
		Head: Head{
			Status:  status,
			Content: ContentXML,
		},
	}
}

// ErrInvalidByteType is returned when the interface is ot a []byte
var ErrInvalidByteType = errors.New("interface not a []byte")

//ByteSocketEncoder provides the basic websocket message encoder for encoding json messages
var ByteSocketEncoder = NewSocketEncoder(func(w *Websocket, t int, bu interface{}) (int, error) {
	var err error
	var size int

	bo, ok := bu.([]byte)

	if !ok {
		return 0, ErrInvalidByteType
	}

	err = w.Conn.WriteMessage(t, bo)

	if err == nil {
		size = len(bo)
	}

	return size, err
})

//ByteSocketDecoder provides the basic websocket decoder which justs returns a decoder
var ByteSocketDecoder = NewSocketDecoder(func(t int, bu []byte) (interface{}, error) {
	return bu, nil
})

// BasicSocketCodec returns a codec using the socket encoder and decoder
var BasicSocketCodec = NewSocketCodec(ByteSocketEncoder, ByteSocketDecoder)
<|endoftext|>


<commit_before>package prompt

// Render to render prompt information from state of Buffer.
type Render struct {
	out                ConsoleWriter
	prefix             string
	livePrefixCallback func() (prefix string, useLivePrefix bool)
	title              string
	row                uint16
	col                uint16

	previousCursor int

	// colors,
	prefixTextColor              Color
	prefixBGColor                Color
	inputTextColor               Color
	inputBGColor                 Color
	previewSuggestionTextColor   Color
	previewSuggestionBGColor     Color
	suggestionTextColor          Color
	suggestionBGColor            Color
	selectedSuggestionTextColor  Color
	selectedSuggestionBGColor    Color
	descriptionTextColor         Color
	descriptionBGColor           Color
	selectedDescriptionTextColor Color
	selectedDescriptionBGColor   Color
	scrollbarThumbColor          Color
	scrollbarBGColor             Color
}

// Setup to initialize console output.
func (r *Render) Setup() {
	if r.title != "" {
		r.out.SetTitle(r.title)
		r.out.Flush()
	}
}

// getCurrentPrefix to get current prefix.
// If live-prefix is enabled, return live-prefix.
func (r *Render) getCurrentPrefix() string {
	if prefix, ok := r.livePrefixCallback(); ok {
		return prefix
	}
	return r.prefix
}

func (r *Render) renderPrefix() {
	r.out.SetColor(r.prefixTextColor, r.prefixBGColor, false)
	r.out.WriteStr(r.getCurrentPrefix())
	r.out.SetColor(DefaultColor, DefaultColor, false)
}

// TearDown to clear title and erasing.
func (r *Render) TearDown() {
	r.out.ClearTitle()
	r.out.EraseDown()
	r.out.Flush()
}

func (r *Render) prepareArea(lines int) {
	for i := 0; i < lines; i++ {
		r.out.ScrollDown()
	}
	for i := 0; i < lines; i++ {
		r.out.ScrollUp()
	}
	return
}

// UpdateWinSize called when window size is changed.
func (r *Render) UpdateWinSize(ws *WinSize) {
	r.row = ws.Row
	r.col = ws.Col
	return
}

func (r *Render) renderWindowTooSmall() {
	r.out.CursorGoTo(0, 0)
	r.out.EraseScreen()
	r.out.SetColor(DarkRed, White, false)
	r.out.WriteStr("Your console window is too small...")
	r.out.Flush()
	return
}

func (r *Render) renderCompletion(buf *Buffer, completions *CompletionManager) {
	suggestions := completions.GetSuggestions()
	if len(completions.GetSuggestions()) == 0 {
		return
	}
	prefix := r.getCurrentPrefix()
	formatted, width := formatSuggestions(
		suggestions,
		int(r.col)-len(prefix)-1, // -1 means a width of scrollbar
	)
	// +1 means a width of scrollbar.
	width++

	windowHeight := len(formatted)
	if windowHeight > int(completions.max) {
		windowHeight = int(completions.max)
	}
	formatted = formatted[completions.verticalScroll : completions.verticalScroll+windowHeight]
	r.prepareArea(windowHeight)

	cursor := len(prefix) + len(buf.Document().TextBeforeCursor())
	x, _ := r.toPos(cursor)
	if x+width >= int(r.col) {
		r.out.CursorBackward(x + width - int(r.col))
	}

	contentHeight := len(completions.tmp)

	fractionVisible := float64(windowHeight) / float64(contentHeight)
	fractionAbove := float64(completions.verticalScroll) / float64(contentHeight)

	scrollbarHeight := int(clamp(float64(windowHeight), 1, float64(windowHeight)*fractionVisible))
	scrollbarTop := int(float64(windowHeight) * fractionAbove)

	isScrollThumb := func(row int) bool {
		return scrollbarTop <= row && row <= scrollbarTop+scrollbarHeight
	}

	selected := completions.selected - completions.verticalScroll
	r.out.SetColor(White, Cyan, false)
	for i := 0; i < windowHeight; i++ {
		r.out.CursorDown(1)
		if i == selected {
			r.out.SetColor(r.selectedSuggestionTextColor, r.selectedSuggestionBGColor, true)
		} else {
			r.out.SetColor(r.suggestionTextColor, r.suggestionBGColor, false)
		}
		r.out.WriteStr(formatted[i].Text)

		if i == selected {
			r.out.SetColor(r.selectedDescriptionTextColor, r.selectedDescriptionBGColor, false)
		} else {
			r.out.SetColor(r.descriptionTextColor, r.descriptionBGColor, false)
		}
		r.out.WriteStr(formatted[i].Description)

		if isScrollThumb(i) {
			r.out.SetColor(DefaultColor, r.scrollbarThumbColor, false)
		} else {
			r.out.SetColor(DefaultColor, r.scrollbarBGColor, false)
		}
		r.out.WriteStr(" ")
		r.out.CursorBackward(width)
	}

	if x+width >= int(r.col) {
		r.out.CursorForward(x + width - int(r.col))
	}

	r.out.CursorUp(windowHeight)
	r.out.SetColor(DefaultColor, DefaultColor, false)
	return
}

// Render renders to the console.
func (r *Render) Render(buffer *Buffer, completion *CompletionManager) {
	line := buffer.Text()
	prefix := r.getCurrentPrefix()
	cursor := len(prefix) + len(line)

	// In situations where a psuedo tty is allocated (e.g. within a docker container),
	// window size via TIOCGWINSZ is not immediately available and will result in 0,0 dimensions.
	if r.col > 0 {
		// Erasing
		r.clear(r.previousCursor)

		// prepare area
		_, y := r.toPos(cursor)

		h := y + 1 + int(completion.max)
		if h > int(r.row) || completionMargin > int(r.col) {
			r.renderWindowTooSmall()
			return
		}
	}

	// Rendering
	r.renderPrefix()
	r.out.SetColor(r.inputTextColor, r.inputBGColor, false)
	r.out.WriteStr(line)
	r.out.SetColor(DefaultColor, DefaultColor, false)

	cursor = r.backward(cursor, len(line)-buffer.CursorPosition)

	r.renderCompletion(buffer, completion)
	if suggest, ok := completion.GetSelectedSuggestion(); ok {
		cursor = r.backward(cursor, len(buffer.Document().GetWordBeforeCursor()))

		r.out.SetColor(r.previewSuggestionTextColor, r.previewSuggestionBGColor, false)
		r.out.WriteStr(suggest.Text)
		r.out.SetColor(DefaultColor, DefaultColor, false)

		cursor += len(suggest.Text)
	}
	r.out.Flush()

	r.previousCursor = cursor
}

// BreakLine to break line.
func (r *Render) BreakLine(buffer *Buffer) {
	// Erasing and Render
	cursor := len(buffer.Document().TextBeforeCursor()) + len(r.getCurrentPrefix())
	r.clear(cursor)
	r.renderPrefix()
	r.out.SetColor(r.inputTextColor, r.inputBGColor, false)
	r.out.WriteStr(buffer.Document().Text + "\n")
	r.out.SetColor(DefaultColor, DefaultColor, false)
	r.out.Flush()

	r.previousCursor = 0
}

func (r *Render) clear(cursor int) {
	r.backward(cursor, cursor)
	r.out.EraseDown()
}

func (r *Render) backward(from, n int) int {
	return r.move(from, from-n)
}

func (r *Render) move(from, to int) int {
	_, fromY := r.toPos(from)
	toX, toY := r.toPos(to)

	r.out.CursorUp(fromY - toY)
	r.out.WriteRaw([]byte{'\r'})
	r.out.CursorForward(toX)
	return to
}

// toPos returns the relative position from the beginning of the string.
// the coordinate system with the beginning of the string as (0,0) and the width as r.col.
// the cursor points to the next character, but it points to that character only at the right end (x == r.col - 1).
// x will not return 0 except for the first row.
func (r *Render) toPos(cursor int) (x, y int) {
	col := int(r.col)

	if cursor > 0 && cursor%col == 0 {
		return col - 1, cursor/col - 1
	}

	return cursor % col, cursor / col
}

func clamp(high, low, x float64) float64 {
	switch {
	case high < x:
		return high
	case x < low:
		return low
	default:
		return x
	}
}
<commit_msg>fix: fix runtime error when r.col == 0<commit_after>package prompt

// Render to render prompt information from state of Buffer.
type Render struct {
	out                ConsoleWriter
	prefix             string
	livePrefixCallback func() (prefix string, useLivePrefix bool)
	title              string
	row                uint16
	col                uint16

	previousCursor int

	// colors,
	prefixTextColor              Color
	prefixBGColor                Color
	inputTextColor               Color
	inputBGColor                 Color
	previewSuggestionTextColor   Color
	previewSuggestionBGColor     Color
	suggestionTextColor          Color
	suggestionBGColor            Color
	selectedSuggestionTextColor  Color
	selectedSuggestionBGColor    Color
	descriptionTextColor         Color
	descriptionBGColor           Color
	selectedDescriptionTextColor Color
	selectedDescriptionBGColor   Color
	scrollbarThumbColor          Color
	scrollbarBGColor             Color
}

// Setup to initialize console output.
func (r *Render) Setup() {
	if r.title != "" {
		r.out.SetTitle(r.title)
		r.out.Flush()
	}
}

// getCurrentPrefix to get current prefix.
// If live-prefix is enabled, return live-prefix.
func (r *Render) getCurrentPrefix() string {
	if prefix, ok := r.livePrefixCallback(); ok {
		return prefix
	}
	return r.prefix
}

func (r *Render) renderPrefix() {
	r.out.SetColor(r.prefixTextColor, r.prefixBGColor, false)
	r.out.WriteStr(r.getCurrentPrefix())
	r.out.SetColor(DefaultColor, DefaultColor, false)
}

// TearDown to clear title and erasing.
func (r *Render) TearDown() {
	r.out.ClearTitle()
	r.out.EraseDown()
	r.out.Flush()
}

func (r *Render) prepareArea(lines int) {
	for i := 0; i < lines; i++ {
		r.out.ScrollDown()
	}
	for i := 0; i < lines; i++ {
		r.out.ScrollUp()
	}
	return
}

// UpdateWinSize called when window size is changed.
func (r *Render) UpdateWinSize(ws *WinSize) {
	r.row = ws.Row
	r.col = ws.Col
	return
}

func (r *Render) renderWindowTooSmall() {
	r.out.CursorGoTo(0, 0)
	r.out.EraseScreen()
	r.out.SetColor(DarkRed, White, false)
	r.out.WriteStr("Your console window is too small...")
	r.out.Flush()
	return
}

func (r *Render) renderCompletion(buf *Buffer, completions *CompletionManager) {
	suggestions := completions.GetSuggestions()
	if len(completions.GetSuggestions()) == 0 {
		return
	}
	prefix := r.getCurrentPrefix()
	formatted, width := formatSuggestions(
		suggestions,
		int(r.col)-len(prefix)-1, // -1 means a width of scrollbar
	)
	// +1 means a width of scrollbar.
	width++

	windowHeight := len(formatted)
	if windowHeight > int(completions.max) {
		windowHeight = int(completions.max)
	}
	formatted = formatted[completions.verticalScroll : completions.verticalScroll+windowHeight]
	r.prepareArea(windowHeight)

	cursor := len(prefix) + len(buf.Document().TextBeforeCursor())
	x, _ := r.toPos(cursor)
	if x+width >= int(r.col) {
		r.out.CursorBackward(x + width - int(r.col))
	}

	contentHeight := len(completions.tmp)

	fractionVisible := float64(windowHeight) / float64(contentHeight)
	fractionAbove := float64(completions.verticalScroll) / float64(contentHeight)

	scrollbarHeight := int(clamp(float64(windowHeight), 1, float64(windowHeight)*fractionVisible))
	scrollbarTop := int(float64(windowHeight) * fractionAbove)

	isScrollThumb := func(row int) bool {
		return scrollbarTop <= row && row <= scrollbarTop+scrollbarHeight
	}

	selected := completions.selected - completions.verticalScroll
	r.out.SetColor(White, Cyan, false)
	for i := 0; i < windowHeight; i++ {
		r.out.CursorDown(1)
		if i == selected {
			r.out.SetColor(r.selectedSuggestionTextColor, r.selectedSuggestionBGColor, true)
		} else {
			r.out.SetColor(r.suggestionTextColor, r.suggestionBGColor, false)
		}
		r.out.WriteStr(formatted[i].Text)

		if i == selected {
			r.out.SetColor(r.selectedDescriptionTextColor, r.selectedDescriptionBGColor, false)
		} else {
			r.out.SetColor(r.descriptionTextColor, r.descriptionBGColor, false)
		}
		r.out.WriteStr(formatted[i].Description)

		if isScrollThumb(i) {
			r.out.SetColor(DefaultColor, r.scrollbarThumbColor, false)
		} else {
			r.out.SetColor(DefaultColor, r.scrollbarBGColor, false)
		}
		r.out.WriteStr(" ")
		r.out.CursorBackward(width)
	}

	if x+width >= int(r.col) {
		r.out.CursorForward(x + width - int(r.col))
	}

	r.out.CursorUp(windowHeight)
	r.out.SetColor(DefaultColor, DefaultColor, false)
	return
}

// Render renders to the console.
func (r *Render) Render(buffer *Buffer, completion *CompletionManager) {
	// In situations where a psuedo tty is allocated (e.g. within a docker container),
	// window size via TIOCGWINSZ is not immediately available and will result in 0,0 dimensions.
	if r.col == 0 {
		return
	}

	// Erasing
	r.clear(r.previousCursor)

	line := buffer.Text()
	prefix := r.getCurrentPrefix()
	cursor := len(prefix) + len(line)

	// prepare area
	_, y := r.toPos(cursor)

	h := y + 1 + int(completion.max)
	if h > int(r.row) || completionMargin > int(r.col) {
		r.renderWindowTooSmall()
		return
	}

	// Rendering
	r.renderPrefix()
	r.out.SetColor(r.inputTextColor, r.inputBGColor, false)
	r.out.WriteStr(line)
	r.out.SetColor(DefaultColor, DefaultColor, false)

	cursor = r.backward(cursor, len(line)-buffer.CursorPosition)

	r.renderCompletion(buffer, completion)
	if suggest, ok := completion.GetSelectedSuggestion(); ok {
		cursor = r.backward(cursor, len(buffer.Document().GetWordBeforeCursor()))

		r.out.SetColor(r.previewSuggestionTextColor, r.previewSuggestionBGColor, false)
		r.out.WriteStr(suggest.Text)
		r.out.SetColor(DefaultColor, DefaultColor, false)

		cursor += len(suggest.Text)
	}
	r.out.Flush()

	r.previousCursor = cursor
}

// BreakLine to break line.
func (r *Render) BreakLine(buffer *Buffer) {
	// Erasing and Render
	cursor := len(buffer.Document().TextBeforeCursor()) + len(r.getCurrentPrefix())
	r.clear(cursor)
	r.renderPrefix()
	r.out.SetColor(r.inputTextColor, r.inputBGColor, false)
	r.out.WriteStr(buffer.Document().Text + "\n")
	r.out.SetColor(DefaultColor, DefaultColor, false)
	r.out.Flush()

	r.previousCursor = 0
}

func (r *Render) clear(cursor int) {
	r.backward(cursor, cursor)
	r.out.EraseDown()
}

func (r *Render) backward(from, n int) int {
	return r.move(from, from-n)
}

func (r *Render) move(from, to int) int {
	_, fromY := r.toPos(from)
	toX, toY := r.toPos(to)

	r.out.CursorUp(fromY - toY)
	r.out.WriteRaw([]byte{'\r'})
	r.out.CursorForward(toX)
	return to
}

// toPos returns the relative position from the beginning of the string.
// the coordinate system with the beginning of the string as (0,0) and the width as r.col.
// the cursor points to the next character, but it points to that character only at the right end (x == r.col - 1).
// x will not return 0 except for the first row.
func (r *Render) toPos(cursor int) (x, y int) {
	col := int(r.col)

	if cursor > 0 && cursor%col == 0 {
		return col - 1, cursor/col - 1
	}

	return cursor % col, cursor / col
}

func clamp(high, low, x float64) float64 {
	switch {
	case high < x:
		return high
	case x < low:
		return low
	default:
		return x
	}
}
<|endoftext|>


<commit_before>package mastodon

import (
	"context"
	"net/http"
)

// Report hold information for mastodon report.
type Report struct {
	ID          int64 `json:"id"`
	ActionTaken bool  `json:"action_taken"`
}

// GetReport return report of the current user.
func (c *Client) GetReport(ctx context.Context) (*Report, error) {
	var reports Report
	err := c.doAPI(ctx, http.MethodGet, "/api/v1/reports", nil, &reports)
	if err != nil {
		return nil, err
	}
	return &reports, nil
}

//  Report reports the report
func (c *Client) Report(ctx context.Context, id int64) (*Report, error) {
	var report Report
	err := c.doAPI(ctx, http.MethodPost, "/api/v1/reports", nil, &report)
	if err != nil {
		return nil, err
	}
	return &report, nil
}
<commit_msg>fix build<commit_after>package mastodon

import (
	"context"
	"net/http"
)

// Report hold information for mastodon report.
type Report struct {
	ID          int64 `json:"id"`
	ActionTaken bool  `json:"action_taken"`
}

// GetReports return report of the current user.
func (c *Client) GetReports(ctx context.Context) ([]*Report, error) {
	var reports []*Report
	err := c.doAPI(ctx, http.MethodGet, "/api/v1/reports", nil, &reports)
	if err != nil {
		return nil, err
	}
	return reports, nil
}

//  Report reports the report
func (c *Client) Report(ctx context.Context, id int64) (*Report, error) {
	var report Report
	err := c.doAPI(ctx, http.MethodPost, "/api/v1/reports", nil, &report)
	if err != nil {
		return nil, err
	}
	return &report, nil
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"path/filepath"
)

// Walkable interface provide the api to pass as filepath.Walk handler.
type Walkable interface {
	Handler(path string, info os.FileInfo, e error) error
}

// Walker is a filepath.Walk handler.
type Walker struct {
	opts          *Options
	ignoreMatcher IgnoreMatcher
}

// NewWalker create a new Walker instance.
func NewWalker(opts *Options) (Walkable, error) {
	matcher, err := NewIgnoreMatcher(opts)
	if err != nil {
		return nil, err
	}

	return &Walker{
		opts:          opts,
		ignoreMatcher: matcher,
	}, nil
}

// Handler is a function for filepath.Walk handler.
func (walker *Walker) Handler(path string, info os.FileInfo, e error) error {
	if info.IsDir() {
		if err := walker.dirFilter(path, info, e); err != nil {
			return err
		}
	}

	return walker.targetHandler(path, info, e)
}

func (walker *Walker) dirFilter(path string, info os.FileInfo, e error) error {
	if info.Name() == ".git" {
		return filepath.SkipDir
	}
	return nil
}

func (walker *Walker) isIgnoreTarget(rel string, info os.FileInfo) bool {
	if walker.ignoreMatcher == nil {
		return false
	}

	return walker.ignoreMatcher.Match(rel, info.IsDir())
}

func (walker *Walker) targetHandler(path string, info os.FileInfo, e error) error {
	rel, err := filepath.Rel(walker.opts.Path, path)
	if err != nil {
		return err
	}

	if walker.isIgnoreTarget(rel, info) {
		if info.IsDir() {
			return filepath.SkipDir
		}
	}

	showMsg("%s\n", rel)
	return nil
}
<commit_msg>:sparkles: Ignore period<commit_after>package main

import (
	"os"
	"path/filepath"
)

// Walkable interface provide the api to pass as filepath.Walk handler.
type Walkable interface {
	Handler(path string, info os.FileInfo, e error) error
}

// Walker is a filepath.Walk handler.
type Walker struct {
	opts          *Options
	ignoreMatcher IgnoreMatcher
}

// NewWalker create a new Walker instance.
func NewWalker(opts *Options) (Walkable, error) {
	matcher, err := NewIgnoreMatcher(opts)
	if err != nil {
		return nil, err
	}

	return &Walker{
		opts:          opts,
		ignoreMatcher: matcher,
	}, nil
}

// Handler is a function for filepath.Walk handler.
func (walker *Walker) Handler(path string, info os.FileInfo, e error) error {
	if info.IsDir() {
		if err := walker.dirFilter(path, info, e); err != nil {
			return err
		}
	}

	return walker.targetHandler(path, info, e)
}

func (walker *Walker) dirFilter(path string, info os.FileInfo, e error) error {
	if info.Name() == ".git" {
		return filepath.SkipDir
	}
	return nil
}

func (walker *Walker) isIgnoreTarget(rel string, info os.FileInfo) bool {
	if walker.ignoreMatcher == nil {
		return false
	}

	return walker.ignoreMatcher.Match(rel, info.IsDir())
}

func (walker *Walker) targetHandler(path string, info os.FileInfo, e error) error {
	rel, err := filepath.Rel(walker.opts.Path, path)
	if err != nil {
		return err
	}

	if walker.isIgnoreTarget(rel, info) {
		if info.IsDir() {
			return filepath.SkipDir
		}
	}

	if rel == "." {
		return nil
	}

	showMsg("%s\n", rel)
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2016 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package web

// Scale 每个错误代码都是从 HTTP 状态码上放大此配数再进行累加的。
const Scale = 1000

// Result 提供了一套用于描述向客户端反馈错误信息的机制。
//
// 对于错误代码的定义是根据 HTTP 状态码进行分类的，
// 比如所有与 400 有关的错误信息，都是以 400 * Scale 为基数的；
// 而与验证有关的都是以 401 * Scale 为基数的。Scale 为一个常量。
//
// 示例：
//  const(
//      BadRequest1 = http.StatusBadRequest * web.Scale + iota
//      BadRequest2
//      BadRequest3
//  )
//
//  func init(){
//      web.SetMessage(BadRequest1, "BadRequest1")
//      web.SetMessage(BadRequest2, "BadRequest2")
//      web.SetMessage(BadRequest3, "BadRequest3")
//  }
// 在 Result.IsError() 为 true 的情况下，也可以将其当作 error 使用。
type Result struct {
	Message string            `json:"message"`
	Code    int               `json:"code"`
	Detail  map[string]string `json:"detail,omitempty"`
}

// NewResult 声明一个新的 Result 实例
func NewResult(code int) *Result {
	return &Result{
		Code:    code,
		Message: Message(code),
		Detail:  make(map[string]string, 2),
	}
}

// NewResultWithDetail 声明一个带 Detail 内容的实例
func NewWithDetail(code int, detail map[string]string) *Result {
	return &Result{
		Code:    code,
		Message: Message(code),
		Detail:  detail,
	}
}

// Add 添加一条详细的错误信息。同名 field 会覆盖。
func (r *Result) Add(field, message string) *Result {
	r.Detail[field] = message
	return r
}

// Error error 接口
func (r *Result) Error() string {
	return r.Message
}

// HasDetail 是否包含详细的错误信息
func (r *Result) HasDetail() bool {
	return len(r.Detail) > 0
}

// IsError 当将 Result 当作 error 实例来用时，需要判断此值是否为 true。
func (r *Result) IsError() bool {
	return r.Status() >= 400
}

// Status 获取与其相对的 HTTP 状态码
func (r *Result) Status() int {
	return r.Code / Scale
}

// CodeNotExists 错误代码不存在时的提示信息
const CodeNotExists = "该错误代码不存在"

// 消息与代码的关联列表
var messages = make(map[int]string, 500)

// SetMessage 关联错误代码和错误信息。
func SetMessage(code int, message string) {
	messages[code] = message
}

// SetMessages 指量执行 SetMessage
func SetMessages(msgs map[int]string) {
	for code, msg := range msgs {
		SetMessage(code, msg)
	}
}

// Message 获取指定代码所表示的错误信息
func Message(code int) string {
	msg, found := messages[code]
	if found {
		return msg
	}

	return CodeNotExists
}
<commit_msg>更正函数名错误<commit_after>// Copyright 2016 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package web

// Scale 每个错误代码都是从 HTTP 状态码上放大此配数再进行累加的。
const Scale = 1000

// Result 提供了一套用于描述向客户端反馈错误信息的机制。
//
// 对于错误代码的定义是根据 HTTP 状态码进行分类的，
// 比如所有与 400 有关的错误信息，都是以 400 * Scale 为基数的；
// 而与验证有关的都是以 401 * Scale 为基数的。Scale 为一个常量。
//
// 示例：
//  const(
//      BadRequest1 = http.StatusBadRequest * web.Scale + iota
//      BadRequest2
//      BadRequest3
//  )
//
//  func init(){
//      web.SetMessage(BadRequest1, "BadRequest1")
//      web.SetMessage(BadRequest2, "BadRequest2")
//      web.SetMessage(BadRequest3, "BadRequest3")
//  }
// 在 Result.IsError() 为 true 的情况下，也可以将其当作 error 使用。
type Result struct {
	Message string            `json:"message"`
	Code    int               `json:"code"`
	Detail  map[string]string `json:"detail,omitempty"`
}

// NewResult 声明一个新的 Result 实例
func NewResult(code int) *Result {
	return &Result{
		Code:    code,
		Message: Message(code),
		Detail:  make(map[string]string, 2),
	}
}

// NewResultWithDetail 声明一个带 Detail 内容的实例
func NewResultWithDetail(code int, detail map[string]string) *Result {
	return &Result{
		Code:    code,
		Message: Message(code),
		Detail:  detail,
	}
}

// Add 添加一条详细的错误信息。同名 field 会覆盖。
func (r *Result) Add(field, message string) *Result {
	r.Detail[field] = message
	return r
}

// Error error 接口
func (r *Result) Error() string {
	return r.Message
}

// HasDetail 是否包含详细的错误信息
func (r *Result) HasDetail() bool {
	return len(r.Detail) > 0
}

// IsError 当将 Result 当作 error 实例来用时，需要判断此值是否为 true。
func (r *Result) IsError() bool {
	return r.Status() >= 400
}

// Status 获取与其相对的 HTTP 状态码
func (r *Result) Status() int {
	return r.Code / Scale
}

// CodeNotExists 错误代码不存在时的提示信息
const CodeNotExists = "该错误代码不存在"

// 消息与代码的关联列表
var messages = make(map[int]string, 500)

// SetMessage 关联错误代码和错误信息。
func SetMessage(code int, message string) {
	messages[code] = message
}

// SetMessages 指量执行 SetMessage
func SetMessages(msgs map[int]string) {
	for code, msg := range msgs {
		SetMessage(code, msg)
	}
}

// Message 获取指定代码所表示的错误信息
func Message(code int) string {
	msg, found := messages[code]
	if found {
		return msg
	}

	return CodeNotExists
}
<|endoftext|>


<commit_before>package tarantool

import (
	"errors"
	"gopkg.in/vmihailenco/msgpack.v2"
	"io"
)

type Result struct {
	ErrorCode int
	Error     error
	Data      [][]interface{}
}

func (r *Result) pack(requestID uint32) (*packedPacket, error) {
	var err error
	var pp *packedPacket

	if r.ErrorCode != OkCode || r.Error != nil {
		if err = r.Error; err == nil {
			err = errors.New("Unknown error")
		}
		var str = err.Error()

		pp = packIprotoError(r.ErrorCode, requestID)
		encoder := msgpack.NewEncoder(&pp.buffer)
		encoder.EncodeMapLen(1)
		encoder.EncodeUint8(KeyError)
		encoder.EncodeString(str)
	} else {
		pp = packIproto(OkCode, requestID)
		encoder := msgpack.NewEncoder(&pp.buffer)
		encoder.EncodeMapLen(1)
		encoder.EncodeUint8(KeyData)
		encoder.EncodeArrayLen(65536) // force encoding as uin32, to be replaced later

		if r.Data != nil {
			for i := 0; i < len(r.Data); i++ {
				if err = encoder.Encode(r.Data[i]); err != nil {
					pp.Release()
					return nil, err
				}
			}
		}

		b := pp.buffer.Bytes()
		if r.Data != nil {
			packBigTo(uint(len(r.Data)), 4, b[3:])
		} else {
			packBigTo(uint(0), 4, b[3:])
		}
	}

	return pp, nil
}

func (r *Result) unpack(rr io.Reader) (err error) {
	var l int
	d := msgpack.NewDecoder(rr)
	if l, err = d.DecodeMapLen(); err != nil {
		return
	}
	for ; l > 0; l-- {
		var cd int
		if cd, err = d.DecodeInt(); err != nil {
			return
		}
		switch cd {
		case KeyData:
			value, err := d.DecodeInterface()
			if err != nil {
				return err
			}
			v := value.([]interface{})
			r.Data = make([]([]interface{}), len(v))
			for i := 0; i < len(v); i++ {
				r.Data[i] = v[i].([]interface{})
			}
		case KeyError:
			errorMessage, err := d.DecodeString()
			if err != nil {
				return err
			}
			r.Error = NewQueryError(errorMessage)
		default:
			if _, err = d.DecodeInterface(); err != nil {
				return
			}
		}
	}
	return
}
<commit_msg>Parse KeyCode in Result<commit_after>package tarantool

import (
	"errors"
	"gopkg.in/vmihailenco/msgpack.v2"
	"io"
)

type Result struct {
	ErrorCode int
	Error     error
	Data      [][]interface{}
}

func (r *Result) pack(requestID uint32) (*packedPacket, error) {
	var err error
	var pp *packedPacket

	if r.ErrorCode != OkCode || r.Error != nil {
		if err = r.Error; err == nil {
			err = errors.New("Unknown error")
		}
		var str = err.Error()

		pp = packIprotoError(r.ErrorCode, requestID)
		encoder := msgpack.NewEncoder(&pp.buffer)
		encoder.EncodeMapLen(1)
		encoder.EncodeUint8(KeyError)
		encoder.EncodeString(str)
	} else {
		pp = packIproto(OkCode, requestID)
		encoder := msgpack.NewEncoder(&pp.buffer)
		encoder.EncodeMapLen(1)
		encoder.EncodeUint8(KeyData)
		encoder.EncodeArrayLen(65536) // force encoding as uin32, to be replaced later

		if r.Data != nil {
			for i := 0; i < len(r.Data); i++ {
				if err = encoder.Encode(r.Data[i]); err != nil {
					pp.Release()
					return nil, err
				}
			}
		}

		b := pp.buffer.Bytes()
		if r.Data != nil {
			packBigTo(uint(len(r.Data)), 4, b[3:])
		} else {
			packBigTo(uint(0), 4, b[3:])
		}
	}

	return pp, nil
}

func (r *Result) unpack(rr io.Reader) (err error) {
	var l int
	d := msgpack.NewDecoder(rr)
	if l, err = d.DecodeMapLen(); err != nil {
		return
	}
	for ; l > 0; l-- {
		var cd int
		if cd, err = d.DecodeInt(); err != nil {
			return
		}
		switch cd {
		case KeyCode:
			if r.ErrorCode, err = d.DecodeInt(); err != nil {
				return
			}
		case KeyData:
			value, err := d.DecodeInterface()
			if err != nil {
				return err
			}
			v := value.([]interface{})
			r.Data = make([]([]interface{}), len(v))
			for i := 0; i < len(v); i++ {
				r.Data[i] = v[i].([]interface{})
			}
		case KeyError:
			errorMessage, err := d.DecodeString()
			if err != nil {
				return err
			}
			r.Error = NewQueryError(errorMessage)
		default:
			if _, err = d.DecodeInterface(); err != nil {
				return
			}
		}
	}
	return
}
<|endoftext|>