<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to you under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package avatica

import (
	"context"
	"database/sql/driver"
	"errors"
	"math"
	"sync"
	"time"

	"github.com/apache/calcite-avatica-go/v5/message"
)

type stmt struct {
	statementID  uint32
	conn         *conn
	parameters   []*message.AvaticaParameter
	handle       message.StatementHandle
	batchUpdates []*message.UpdateBatch
	sync.Mutex
}

// Close closes a statement
func (s *stmt) Close() error {

	if s.conn.connectionId == "" {
		return driver.ErrBadConn
	}

	if s.conn.config.batching {
		_, err := s.conn.httpClient.post(context.Background(), &message.ExecuteBatchRequest{
			ConnectionId: s.conn.connectionId,
			StatementId:  s.statementID,
			Updates:      s.batchUpdates,
		})
		if err != nil {
			return s.conn.avaticaErrorToResponseErrorOrError(err)
		}
	}

	_, err := s.conn.httpClient.post(context.Background(), &message.CloseStatementRequest{
		ConnectionId: s.conn.connectionId,
		StatementId:  s.statementID,
	})

	if err != nil {
		return s.conn.avaticaErrorToResponseErrorOrError(err)
	}

	return nil
}

// NumInput returns the number of placeholder parameters.
//
// If NumInput returns >= 0, the sql package will sanity check
// argument counts from callers and return errors to the caller
// before the statement's Exec or Query methods are called.
//
// NumInput may also return -1, if the driver doesn't know
// its number of placeholders. In that case, the sql package
// will not sanity check Exec or Query argument counts.
func (s *stmt) NumInput() int {
	return len(s.parameters)
}

// Exec executes a query that doesn't return rows, such
// as an INSERT or UPDATE.
func (s *stmt) Exec(args []driver.Value) (driver.Result, error) {
	list := driverValueToNamedValue(args)
	return s.exec(context.Background(), list)
}

func (s *stmt) exec(ctx context.Context, args []namedValue) (driver.Result, error) {

	if s.conn.connectionId == "" {
		return nil, driver.ErrBadConn
	}

	values := s.parametersToTypedValues(args)

	if s.conn.config.batching {
		s.Lock()
		defer s.Unlock()

		s.batchUpdates = append(s.batchUpdates, &message.UpdateBatch{
			ParameterValues: values,
		})
		return &result{
			affectedRows: -1,
		}, nil
	}

	msg := &message.ExecuteRequest{
		StatementHandle:    &s.handle,
		ParameterValues:    values,
		FirstFrameMaxSize:  s.conn.config.frameMaxSize,
		HasParameterValues: true,
	}

	if s.conn.config.frameMaxSize <= -1 {
		msg.DeprecatedFirstFrameMaxSize = math.MaxInt64
	} else {
		msg.DeprecatedFirstFrameMaxSize = uint64(s.conn.config.frameMaxSize)
	}

	res, err := s.conn.httpClient.post(ctx, msg)

	if err != nil {
		return nil, s.conn.avaticaErrorToResponseErrorOrError(err)
	}

	results := res.(*message.ExecuteResponse).Results

	if len(results) <= 0 {
		return nil, errors.New("empty ResultSet in ExecuteResponse")
	}

	// Currently there is only 1 ResultSet per response
	changed := int64(results[0].UpdateCount)

	return &result{
		affectedRows: changed,
	}, nil
}

// Query executes a query that may return rows, such as a
// SELECT.
func (s *stmt) Query(args []driver.Value) (driver.Rows, error) {
	list := driverValueToNamedValue(args)
	return s.query(context.Background(), list)
}

func (s *stmt) query(ctx context.Context, args []namedValue) (driver.Rows, error) {
	if s.conn.connectionId == "" {
		return nil, driver.ErrBadConn
	}

	msg := &message.ExecuteRequest{
		StatementHandle:    &s.handle,
		ParameterValues:    s.parametersToTypedValues(args),
		FirstFrameMaxSize:  s.conn.config.frameMaxSize,
		HasParameterValues: true,
	}

	if s.conn.config.frameMaxSize <= -1 {
		msg.DeprecatedFirstFrameMaxSize = math.MaxInt64
	} else {
		msg.DeprecatedFirstFrameMaxSize = uint64(s.conn.config.frameMaxSize)
	}

	res, err := s.conn.httpClient.post(ctx, msg)

	if err != nil {
		return nil, s.conn.avaticaErrorToResponseErrorOrError(err)
	}

	resultSet := res.(*message.ExecuteResponse).Results

	return newRows(s.conn, s.statementID, resultSet), nil
}

func (s *stmt) parametersToTypedValues(vals []namedValue) []*message.TypedValue {

	var result []*message.TypedValue

	for i, val := range vals {
		typed := message.TypedValue{}
		if val.Value == nil {
			typed.Null = true
			typed.Type = message.Rep_NULL
		} else {

			switch v := val.Value.(type) {
			case int64:
				typed.Type = message.Rep_LONG
				typed.NumberValue = v
			case float64:
				typed.Type = message.Rep_DOUBLE
				typed.DoubleValue = v
			case bool:
				typed.Type = message.Rep_BOOLEAN
				typed.BoolValue = v
			case []byte:
				typed.Type = message.Rep_BYTE_STRING
				typed.BytesValue = v
			case string:

				if s.parameters[i].TypeName == "DECIMAL" {
					typed.Type = message.Rep_BIG_DECIMAL
				} else {
					typed.Type = message.Rep_STRING
				}
				typed.StringValue = v

			case time.Time:
				avaticaParameter := s.parameters[i]

				switch avaticaParameter.TypeName {
				case "TIME", "UNSIGNED_TIME":
					typed.Type = message.Rep_JAVA_SQL_TIME

					// Because a location can have multiple time zones due to daylight savings,
					// we need to be explicit and get the offset
					zone, offset := v.Zone()

					// Calculate milliseconds since 00:00:00.000
					base := time.Date(v.Year(), v.Month(), v.Day(), 0, 0, 0, 0, time.FixedZone(zone, offset))
					typed.NumberValue = v.Sub(base).Nanoseconds() / int64(time.Millisecond)

				case "DATE", "UNSIGNED_DATE":
					typed.Type = message.Rep_JAVA_SQL_DATE

					// Because a location can have multiple time zones due to daylight savings,
					// we need to be explicit and get the offset
					zone, offset := v.Zone()

					// Calculate number of days since 1970/1/1
					base := time.Date(1970, 1, 1, 0, 0, 0, 0, time.FixedZone(zone, offset))
					typed.NumberValue = int64(v.Sub(base) / (24 * time.Hour))

				case "TIMESTAMP", "UNSIGNED_TIMESTAMP":
					typed.Type = message.Rep_JAVA_SQL_TIMESTAMP

					// Because a location can have multiple time zones due to daylight savings,
					// we need to be explicit and get the offset
					zone, offset := v.Zone()

					// Calculate number of milliseconds since 1970-01-01 00:00:00.000
					base := time.Date(1970, 1, 1, 0, 0, 0, 0, time.FixedZone(zone, offset))
					typed.NumberValue = v.Sub(base).Nanoseconds() / int64(time.Millisecond)
				}
			}
		}

		result = append(result, &typed)
	}

	return result
}
<commit_msg>[CALCITE-5320] Switch from deprecated_first_frame_max_size to first_frame_max_size protobuf member for setting the first frame max size<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to you under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package avatica

import (
	"context"
	"database/sql/driver"
	"errors"
	"math"
	"sync"
	"time"

	"github.com/apache/calcite-avatica-go/v5/message"
)

type stmt struct {
	statementID  uint32
	conn         *conn
	parameters   []*message.AvaticaParameter
	handle       message.StatementHandle
	batchUpdates []*message.UpdateBatch
	sync.Mutex
}

// Close closes a statement
func (s *stmt) Close() error {

	if s.conn.connectionId == "" {
		return driver.ErrBadConn
	}

	if s.conn.config.batching {
		_, err := s.conn.httpClient.post(context.Background(), &message.ExecuteBatchRequest{
			ConnectionId: s.conn.connectionId,
			StatementId:  s.statementID,
			Updates:      s.batchUpdates,
		})
		if err != nil {
			return s.conn.avaticaErrorToResponseErrorOrError(err)
		}
	}

	_, err := s.conn.httpClient.post(context.Background(), &message.CloseStatementRequest{
		ConnectionId: s.conn.connectionId,
		StatementId:  s.statementID,
	})

	if err != nil {
		return s.conn.avaticaErrorToResponseErrorOrError(err)
	}

	return nil
}

// NumInput returns the number of placeholder parameters.
//
// If NumInput returns >= 0, the sql package will sanity check
// argument counts from callers and return errors to the caller
// before the statement's Exec or Query methods are called.
//
// NumInput may also return -1, if the driver doesn't know
// its number of placeholders. In that case, the sql package
// will not sanity check Exec or Query argument counts.
func (s *stmt) NumInput() int {
	return len(s.parameters)
}

// Exec executes a query that doesn't return rows, such
// as an INSERT or UPDATE.
func (s *stmt) Exec(args []driver.Value) (driver.Result, error) {
	list := driverValueToNamedValue(args)
	return s.exec(context.Background(), list)
}

func (s *stmt) exec(ctx context.Context, args []namedValue) (driver.Result, error) {

	if s.conn.connectionId == "" {
		return nil, driver.ErrBadConn
	}

	values := s.parametersToTypedValues(args)

	if s.conn.config.batching {
		s.Lock()
		defer s.Unlock()

		s.batchUpdates = append(s.batchUpdates, &message.UpdateBatch{
			ParameterValues: values,
		})
		return &result{
			affectedRows: -1,
		}, nil
	}

	msg := &message.ExecuteRequest{
		StatementHandle:    &s.handle,
		ParameterValues:    values,
		FirstFrameMaxSize:  s.conn.config.frameMaxSize,
		HasParameterValues: true,
	}

	if s.conn.config.frameMaxSize <= -1 {
		msg.FirstFrameMaxSize = math.MaxInt32
	} else {
		msg.FirstFrameMaxSize = s.conn.config.frameMaxSize
	}

	res, err := s.conn.httpClient.post(ctx, msg)

	if err != nil {
		return nil, s.conn.avaticaErrorToResponseErrorOrError(err)
	}

	results := res.(*message.ExecuteResponse).Results

	if len(results) <= 0 {
		return nil, errors.New("empty ResultSet in ExecuteResponse")
	}

	// Currently there is only 1 ResultSet per response
	changed := int64(results[0].UpdateCount)

	return &result{
		affectedRows: changed,
	}, nil
}

// Query executes a query that may return rows, such as a
// SELECT.
func (s *stmt) Query(args []driver.Value) (driver.Rows, error) {
	list := driverValueToNamedValue(args)
	return s.query(context.Background(), list)
}

func (s *stmt) query(ctx context.Context, args []namedValue) (driver.Rows, error) {
	if s.conn.connectionId == "" {
		return nil, driver.ErrBadConn
	}

	msg := &message.ExecuteRequest{
		StatementHandle:    &s.handle,
		ParameterValues:    s.parametersToTypedValues(args),
		FirstFrameMaxSize:  s.conn.config.frameMaxSize,
		HasParameterValues: true,
	}

	if s.conn.config.frameMaxSize <= -1 {
		msg.FirstFrameMaxSize = math.MaxInt32
	} else {
		msg.FirstFrameMaxSize = s.conn.config.frameMaxSize
	}

	res, err := s.conn.httpClient.post(ctx, msg)

	if err != nil {
		return nil, s.conn.avaticaErrorToResponseErrorOrError(err)
	}

	resultSet := res.(*message.ExecuteResponse).Results

	return newRows(s.conn, s.statementID, resultSet), nil
}

func (s *stmt) parametersToTypedValues(vals []namedValue) []*message.TypedValue {

	var result []*message.TypedValue

	for i, val := range vals {
		typed := message.TypedValue{}
		if val.Value == nil {
			typed.Null = true
			typed.Type = message.Rep_NULL
		} else {

			switch v := val.Value.(type) {
			case int64:
				typed.Type = message.Rep_LONG
				typed.NumberValue = v
			case float64:
				typed.Type = message.Rep_DOUBLE
				typed.DoubleValue = v
			case bool:
				typed.Type = message.Rep_BOOLEAN
				typed.BoolValue = v
			case []byte:
				typed.Type = message.Rep_BYTE_STRING
				typed.BytesValue = v
			case string:

				if s.parameters[i].TypeName == "DECIMAL" {
					typed.Type = message.Rep_BIG_DECIMAL
				} else {
					typed.Type = message.Rep_STRING
				}
				typed.StringValue = v

			case time.Time:
				avaticaParameter := s.parameters[i]

				switch avaticaParameter.TypeName {
				case "TIME", "UNSIGNED_TIME":
					typed.Type = message.Rep_JAVA_SQL_TIME

					// Because a location can have multiple time zones due to daylight savings,
					// we need to be explicit and get the offset
					zone, offset := v.Zone()

					// Calculate milliseconds since 00:00:00.000
					base := time.Date(v.Year(), v.Month(), v.Day(), 0, 0, 0, 0, time.FixedZone(zone, offset))
					typed.NumberValue = v.Sub(base).Nanoseconds() / int64(time.Millisecond)

				case "DATE", "UNSIGNED_DATE":
					typed.Type = message.Rep_JAVA_SQL_DATE

					// Because a location can have multiple time zones due to daylight savings,
					// we need to be explicit and get the offset
					zone, offset := v.Zone()

					// Calculate number of days since 1970/1/1
					base := time.Date(1970, 1, 1, 0, 0, 0, 0, time.FixedZone(zone, offset))
					typed.NumberValue = int64(v.Sub(base) / (24 * time.Hour))

				case "TIMESTAMP", "UNSIGNED_TIMESTAMP":
					typed.Type = message.Rep_JAVA_SQL_TIMESTAMP

					// Because a location can have multiple time zones due to daylight savings,
					// we need to be explicit and get the offset
					zone, offset := v.Zone()

					// Calculate number of milliseconds since 1970-01-01 00:00:00.000
					base := time.Date(1970, 1, 1, 0, 0, 0, 0, time.FixedZone(zone, offset))
					typed.NumberValue = v.Sub(base).Nanoseconds() / int64(time.Millisecond)
				}
			}
		}

		result = append(result, &typed)
	}

	return result
}
<|endoftext|>


<commit_before>package ostent
import (
	"io"
	"os"
	"log"
	"bufio"
	"strings"
)

func init() {
	lf := logFiltered{}
	var reader io.Reader
	reader, lf.writer = io.Pipe()
	lf.scanner = bufio.NewScanner(reader)
	go lf.read()
	log.SetOutput(&lf)
}

type logFiltered struct{
	writer  io.Writer
	scanner *bufio.Scanner
	ping chan bool
}

func (lf *logFiltered) Write(p []byte) (int, error) {
	return lf.writer.Write(p)
}

func (lf *logFiltered) read() {
	for {
		if !lf.scanner.Scan() {
			if err := lf.scanner.Err(); err != nil {
				log.New(os.Stderr, "", log.LstdFlags).Printf("bufio.Scanner.Scan Err: %s", err)
			}
			continue
		}
		text := lf.scanner.Text()
		if strings.Contains(text, " handling ") {
			continue
		}
		os.Stderr.WriteString(text +"\n")
	}
}

<commit_msg>stdlogger.go: gofmt<commit_after>package ostent

import (
	"io"
	"os"
	"log"
	"bufio"
	"strings"
)

func init() {
	lf := logFiltered{}
	var reader io.Reader
	reader, lf.writer = io.Pipe()
	lf.scanner = bufio.NewScanner(reader)
	go lf.read()
	log.SetOutput(&lf)
}

type logFiltered struct {
	writer  io.Writer
	scanner *bufio.Scanner
}

func (lf *logFiltered) Write(p []byte) (int, error) {
	return lf.writer.Write(p)
}

func (lf *logFiltered) read() {
	for {
		if !lf.scanner.Scan() {
			if err := lf.scanner.Err(); err != nil {
				log.New(os.Stderr, "", log.LstdFlags).Printf("bufio.Scanner.Scan Err: %s", err)
			}
			continue
		}
		text := lf.scanner.Text()
		if strings.Contains(text, " handling ") {
			continue
		}
		os.Stderr.WriteString(text + "\n")
	}
}
<|endoftext|>


<commit_before>// Package stopwatch provides a timer that implements common stopwatch
// functionality.
package stopwatch

import (
	"fmt"
	"log"
	"strings"
	"time"
)

// Stopwatch implements the stopwatch functionality. It is not threadsafe by
// design and should be protected when there is a need for.
type Stopwatch struct {
	start, stop, lap time.Time
	laps             []time.Duration
}

// New creates a new Stopwatch. To start the stopwatch Start() should be invoked.
func New() *Stopwatch {
	return &Stopwatch{
		laps: make([]time.Duration, 0),
	}
}

// Start creates a new stopwatch with starting time offset by a user defined
// value. Negative offsets result in a countdown prior to the start of the
// stopwatch. A zero offset starts the stopwatch immediately.
func Start(offset time.Duration) *Stopwatch {
	s := &Stopwatch{
		start: time.Now().Add(offset),
		lap:   time.Now().Add(offset),
		laps:  make([]time.Duration, 0),
	}
	return s
}

// IsStopped shows whether the stopwatch is stopped or not.
func (s *Stopwatch) IsStopped() bool { return s.stop.After(s.start) }

// IsReseted shows whether the stopwatch is reseted or not.
func (s *Stopwatch) IsReseted() bool { return s.start.IsZero() }

// ElapsedTime returns the duration between the start and current time.
func (s *Stopwatch) ElapsedTime() time.Duration {
	if s.IsStopped() {
		return s.stop.Sub(s.start)
	}

	if s.IsReseted() {
		return time.Duration(0)
	}

	return time.Since(s.start)
}

// Print calls fmt.Printf() with the given string and the elapsed time attached.
// Useful to use with a defer statement.
// Example : defer Start().Print("myFunction")
// Output  :  myFunction - elapsed: 2.000629842s
func (s *Stopwatch) Print(msg string) {
	fmt.Printf("%s - elapsed: %s\n", msg, s.ElapsedTime().String())
}

// Log calls log.Printf() with the given string and the elapsed time attached.
// Useful to use with a defer statement.
// Example : defer Start().Log("myFunction")
// Output: 2014/02/10 00:44:56 myFunction - elapsed: 2.000169591s
func (s *Stopwatch) Log(msg string) {
	log.Printf("%s - elapsed: %s\n", msg, s.ElapsedTime().String())
}

// Stop stops the timer. To resume the timer Start() needs to be called again.
func (s *Stopwatch) Stop() {
	s.stop = time.Now()
}

// Start resumes or starts the timer. If a Stop() was invoked it resumes the
// timer. If a Reset() was invoked it starts a new session with the given
// offset.
func (s *Stopwatch) Start(offset time.Duration) {
	if s.IsReseted() {
		*s = *Start(offset)
	} else { //stopped
		s.start = s.start.Add(time.Since(s.stop))
	}
}

// Reset resets the timer. It needs to be started again with the Start()
// method.
func (s *Stopwatch) Reset() {
	s.start, s.stop, s.lap = time.Time{}, time.Time{}, time.Time{}
	s.laps = nil
}

// Lap takes and stores the current lap time and returns the elapsed time
// since the latest lap.
func (s *Stopwatch) Lap() time.Duration {
	// There is no lap if the timer is resetted or stoped
	if s.IsStopped() || s.IsReseted() {
		return time.Duration(0)
	}

	lap := time.Since(s.lap)
	s.lap = time.Now()
	s.laps = append(s.laps, lap)

	return lap
}

// Laps returns a slice of all completed laps.
func (s *Stopwatch) Laps() []time.Duration {
	laps := make([]time.Duration, len(s.laps))
	copy(laps, s.laps)
	return laps
}

// String representation of a single Stopwatch instance.
func (s *Stopwatch) String() string {
	return fmt.Sprintf("[start: %s current: %s elapsed: %s]",
		s.start.Format(time.Stamp), time.Now().Format(time.Stamp), s.ElapsedTime())
}

// MarshalJSON implements the json.Marshaler interface. The elapsed time is
// quoted as a string and is in the form "72h3m0.5s". For more info please
// refer to time.Duration.String().
func (s *Stopwatch) MarshalJSON() ([]byte, error) {
	return []byte(`"` + s.ElapsedTime().String() + `"`), nil
}

// UnmarshalJSON implements the json.Unmarshaler interface. The elapsed time
// is expected to be a string that can be successful parsed with
// time.ParseDuration.
func (s *Stopwatch) UnmarshalJSON(data []byte) (err error) {
	unquoted := strings.Replace(string(data), "\"", "", -1)
	d, err := time.ParseDuration(unquoted)
	if err != nil {
		return err
	}

	// set the start time based on the elapsed time
	s.start = time.Now().Add(-d)
	return nil
}
<commit_msg>More fixes<commit_after>// Package stopwatch provides a timer that implements common stopwatch
// functionality.
package stopwatch

import (
	"fmt"
	"log"
	"strings"
	"time"
)

// Stopwatch implements the stopwatch functionality. It is not threadsafe by
// design and should be protected when there is a need for.
type Stopwatch struct {
	start, stop, lap time.Time
	laps             []time.Duration
}

// New creates a new Stopwatch. To start the stopwatch Start() should be invoked.
func New() *Stopwatch {
	return &Stopwatch{
		laps: make([]time.Duration, 0),
	}
}

// Start creates a new stopwatch with starting time offset by a user defined
// value. Negative offsets result in a countdown prior to the start of the
// stopwatch. A zero offset starts the stopwatch immediately.
func Start(offset time.Duration) *Stopwatch {
	t := time.Now().Add(offset)
	s := &Stopwatch{
		start: t,
		lap:   t,
		laps:  make([]time.Duration, 0),
	}
	return s
}

// IsStopped shows whether the stopwatch is stopped or not.
func (s *Stopwatch) IsStopped() bool { return s.stop.After(s.start) }

// IsReseted shows whether the stopwatch is reseted or not.
func (s *Stopwatch) IsReseted() bool { return s.start.IsZero() }

// ElapsedTime returns the duration between the start and current time.
func (s *Stopwatch) ElapsedTime() time.Duration {
	if s.IsStopped() {
		return s.stop.Sub(s.start)
	}

	if s.IsReseted() {
		return time.Duration(0)
	}

	return time.Since(s.start)
}

// Print calls fmt.Printf() with the given string and the elapsed time attached.
// Useful to use with a defer statement.
// Example : defer Start().Print("myFunction")
// Output  :  myFunction - elapsed: 2.000629842s
func (s *Stopwatch) Print(msg string) {
	fmt.Printf("%s - elapsed: %s\n", msg, s.ElapsedTime())
}

// Log calls log.Printf() with the given string and the elapsed time attached.
// Useful to use with a defer statement.
// Example : defer Start().Log("myFunction")
// Output: 2014/02/10 00:44:56 myFunction - elapsed: 2.000169591s
func (s *Stopwatch) Log(msg string) {
	log.Printf("%s - elapsed: %s\n", msg, s.ElapsedTime())
}

// Stop stops the timer. To resume the timer Start() needs to be called again.
func (s *Stopwatch) Stop() {
	s.stop = time.Now()
}

// Start resumes or starts the timer. If a Stop() was invoked it resumes the
// timer. If a Reset() was invoked it starts a new session with the given
// offset.
func (s *Stopwatch) Start(offset time.Duration) {
	if s.IsReseted() {
		*s = *Start(offset)
	} else { //stopped
		s.start = s.start.Add(time.Since(s.stop))
	}
}

// Reset resets the timer. It needs to be started again with the Start()
// method.
func (s *Stopwatch) Reset() {
	s.start, s.stop, s.lap = time.Time{}, time.Time{}, time.Time{}
	s.laps = nil
}

// Lap takes and stores the current lap time and returns the elapsed time
// since the latest lap.
func (s *Stopwatch) Lap() time.Duration {
	// There is no lap if the timer is resetted or stoped
	if s.IsStopped() || s.IsReseted() {
		return time.Duration(0)
	}

	lap := time.Since(s.lap)
	s.lap = time.Now()
	s.laps = append(s.laps, lap)

	return lap
}

// Laps returns a slice of all completed laps.
func (s *Stopwatch) Laps() []time.Duration {
	laps := make([]time.Duration, len(s.laps))
	copy(laps, s.laps)
	return laps
}

// String representation of a single Stopwatch instance.
func (s *Stopwatch) String() string {
	return fmt.Sprintf("[start: %s current: %s elapsed: %s]",
		s.start.Format(time.Stamp), time.Now().Format(time.Stamp), s.ElapsedTime())
}

// MarshalJSON implements the json.Marshaler interface. The elapsed time is
// quoted as a string and is in the form "72h3m0.5s". For more info please
// refer to time.Duration.String().
func (s *Stopwatch) MarshalJSON() ([]byte, error) {
	return []byte(`"` + s.ElapsedTime().String() + `"`), nil
}

// UnmarshalJSON implements the json.Unmarshaler interface. The elapsed time
// is expected to be a string that can be successful parsed with
// time.ParseDuration.
func (s *Stopwatch) UnmarshalJSON(data []byte) (err error) {
	unquoted := strings.Replace(string(data), "\"", "", -1)
	d, err := time.ParseDuration(unquoted)
	if err != nil {
		return err
	}

	// set the start time based on the elapsed time
	s.start = time.Now().Add(-d)
	return nil
}
<|endoftext|>


<commit_before>package m3u8

/*
 Part of M3U8 parser & generator library.
 This file defines data structures related to package.

 Copyright 2013-2017 The Project Developers.
 See the AUTHORS and LICENSE files at the top-level directory of this distribution
 and at https://github.com/grafov/m3u8/

 ॐ तारे तुत्तारे तुरे स्व
*/

import (
	"bytes"
	"io"
	"time"
)

const (
	/*
		Compatibility rules described in section 7:
		Clients and servers MUST implement protocol version 2 or higher to use:
		   o  The IV attribute of the EXT-X-KEY tag.
		   Clients and servers MUST implement protocol version 3 or higher to use:
		   o  Floating-point EXTINF duration values.
		   Clients and servers MUST implement protocol version 4 or higher to use:
		   o  The EXT-X-BYTERANGE tag.
		   o  The EXT-X-I-FRAME-STREAM-INF tag.
		   o  The EXT-X-I-FRAMES-ONLY tag.
		   o  The EXT-X-MEDIA tag.
		   o  The AUDIO and VIDEO attributes of the EXT-X-STREAM-INF tag.
	*/
	minver   = uint8(3)
	DATETIME = time.RFC3339Nano // Format for EXT-X-PROGRAM-DATE-TIME defined in section 3.4.5
)

type ListType uint

const (
	// use 0 for not defined type
	MASTER ListType = iota + 1
	MEDIA
)

// for EXT-X-PLAYLIST-TYPE tag
type MediaType uint

const (
	// use 0 for not defined type
	EVENT MediaType = iota + 1
	VOD
)

// SCTE35Syntax defines the format of the SCTE-35 cue points which do not use
// the draft-pantos-http-live-streaming-19 EXT-X-DATERANGE tag and instead
// have their own custom tags
type SCTE35Syntax uint

const (
	// SCTE35_67_2014 will be the default due to backwards compatibility reasons.
	SCTE35_67_2014 SCTE35Syntax = iota // SCTE35_67_2014 defined in http://www.scte.org/documents/pdf/standards/SCTE%2067%202014.pdf
	SCTE35_OATCLS                      // SCTE35_OATCLS is a non-standard but common format
)

// SCTE35CueType defines the type of cue point, used by readers and writers to
// write a different syntax
type SCTE35CueType uint

const (
	SCTE35Cue_Start SCTE35CueType = iota // SCTE35Cue_Start indicates an out cue point
	SCTE35Cue_Mid                        // SCTE35Cue_Mid indicates a segment between start and end cue points
	SCTE35Cue_End                        // SCTE35Cue_End indicates an in cue point
)

/*
 This structure represents a single bitrate playlist aka media playlist.
 It related to both a simple media playlists and a sliding window media playlists.
 URI lines in the Playlist point to media segments.

 Simple Media Playlist file sample:

   #EXTM3U
   #EXT-X-VERSION:3
   #EXT-X-TARGETDURATION:5220
   #EXTINF:5219.2,
   http://media.example.com/entire.ts
   #EXT-X-ENDLIST

 Sample of Sliding Window Media Playlist, using HTTPS:

   #EXTM3U
   #EXT-X-VERSION:3
   #EXT-X-TARGETDURATION:8
   #EXT-X-MEDIA-SEQUENCE:2680

   #EXTINF:7.975,
   https://priv.example.com/fileSequence2680.ts
   #EXTINF:7.941,
   https://priv.example.com/fileSequence2681.ts
   #EXTINF:7.975,
   https://priv.example.com/fileSequence2682.ts
*/
type MediaPlaylist struct {
	TargetDuration   float64
	SeqNo            uint64 // EXT-X-MEDIA-SEQUENCE
	Segments         []*MediaSegment
	Args             string // optional arguments placed after URIs (URI?Args)
	Iframe           bool   // EXT-X-I-FRAMES-ONLY
	Closed           bool   // is this VOD (closed) or Live (sliding) playlist?
	MediaType        MediaType
	DiscontinuitySeq uint32 // EXT-X-DISCONTINUITY-SEQUENCE
	durationAsInt    bool   // output durations as integers of floats?
	keyformat        int
	winsize          uint // max number of segments displayed in an encoded playlist; need set to zero for VOD playlists
	capacity         uint // total capacity of slice used for the playlist
	head             uint // head of FIFO, we add segments to head
	tail             uint // tail of FIFO, we remove segments from tail
	count            uint // number of segments added to the playlist
	buf              bytes.Buffer
	ver              uint8
	Key              *Key // EXT-X-KEY is optional encryption key displayed before any segments (default key for the playlist)
	Map              *Map // EXT-X-MAP is optional tag specifies how to obtain the Media Initialization Section (default map for the playlist)
	WV               *WV  // Widevine related tags outside of M3U8 specs
}

/*
 This structure represents a master playlist which combines media playlists for multiple bitrates.
 URI lines in the playlist identify media playlists.
 Sample of Master Playlist file:

   #EXTM3U
   #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1280000
   http://example.com/low.m3u8
   #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=2560000
   http://example.com/mid.m3u8
   #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=7680000
   http://example.com/hi.m3u8
   #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=65000,CODECS="mp4a.40.5"
   http://example.com/audio-only.m3u8
*/
type MasterPlaylist struct {
	Variants      []*Variant
	Args          string // optional arguments placed after URI (URI?Args)
	CypherVersion string // non-standard tag for Widevine (see also WV struct)
	buf           bytes.Buffer
	ver           uint8
}

// This structure represents variants for master playlist.
// Variants included in a master playlist and point to media playlists.
type Variant struct {
	URI       string
	Chunklist *MediaPlaylist
	VariantParams
}

// This structure represents additional parameters for a variant
// used in EXT-X-STREAM-INF and EXT-X-I-FRAME-STREAM-INF
type VariantParams struct {
	ProgramId        uint32
	Bandwidth        uint32
	AverageBandwidth uint32 // EXT-X-STREAM-INF only
	Codecs           string
	Resolution       string
	Audio            string // EXT-X-STREAM-INF only
	Video            string
	Subtitles        string         // EXT-X-STREAM-INF only
	Captions         string         // EXT-X-STREAM-INF only
	Name             string         // EXT-X-STREAM-INF only (non standard Wowza/JWPlayer extension to name the variant/quality in UA)
	FrameRate        float64        // EXT-X-STREAM-INF
	Iframe           bool           // EXT-X-I-FRAME-STREAM-INF
	Alternatives     []*Alternative // EXT-X-MEDIA
}

// This structure represents EXT-X-MEDIA tag in variants.
type Alternative struct {
	GroupId         string
	URI             string
	Type            string
	Language        string
	Name            string
	Default         bool
	Autoselect      string
	Forced          string
	Characteristics string
	Subtitles       string
}

// This structure represents a media segment included in a media playlist.
// Media segment may be encrypted.
// Widevine supports own tags for encryption metadata.
type MediaSegment struct {
	SeqId           uint64
	Title           string // optional second parameter for EXTINF tag
	URI             string
	Duration        float64   // first parameter for EXTINF tag; duration must be integers if protocol version is less than 3 but we are always keep them float
	Limit           int64     // EXT-X-BYTERANGE <n> is length in bytes for the file under URI
	Offset          int64     // EXT-X-BYTERANGE [@o] is offset from the start of the file under URI
	Key             *Key      // EXT-X-KEY displayed before the segment and means changing of encryption key (in theory each segment may have own key)
	Map             *Map      // EXT-X-MAP displayed before the segment
	Discontinuity   bool      // EXT-X-DISCONTINUITY indicates an encoding discontinuity between the media segment that follows it and the one that preceded it (i.e. file format, number and type of tracks, encoding parameters, encoding sequence, timestamp sequence)
	SCTE            *SCTE     // SCTE-35 used for Ad signaling in HLS
	ProgramDateTime time.Time // EXT-X-PROGRAM-DATE-TIME tag associates the first sample of a media segment with an absolute date and/or time
}

// SCTE holds custom, non EXT-X-DATERANGE, SCTE-35 tags
type SCTE struct {
	Syntax  SCTE35Syntax  // Syntax defines the format of the SCTE-35 cue tag
	CueType SCTE35CueType // CueType defines whether the cue is a start, mid, end (if applicable)
	Cue     string
	ID      string
	Time    float64
	Elapsed float64
}

// This structure represents information about stream encryption.
//
// Realizes EXT-X-KEY tag.
type Key struct {
	Method            string
	URI               string
	IV                string
	Keyformat         string
	Keyformatversions string
}

// This structure represents specifies how to obtain the Media
// Initialization Section required to parse the applicable
// Media Segments.

// It applies to every Media Segment that appears after it in the
// Playlist until the next EXT-X-MAP tag or until the end of the
// playlist.
//
// Realizes EXT-MAP tag.
type Map struct {
	URI    string
	Limit  int64 // <n> is length in bytes for the file under URI
	Offset int64 // [@o] is offset from the start of the file under URI
}

// This structure represents metadata  for Google Widevine playlists.
// This format not described in IETF draft but provied by Widevine Live Packager as
// additional tags with #WV-prefix.
type WV struct {
	AudioChannels          uint
	AudioFormat            uint
	AudioProfileIDC        uint
	AudioSampleSize        uint
	AudioSamplingFrequency uint
	CypherVersion          string
	ECM                    string
	VideoFormat            uint
	VideoFrameRate         uint
	VideoLevelIDC          uint
	VideoProfileIDC        uint
	VideoResolution        string
	VideoSAR               string
}

// Interface applied to various playlist types.
type Playlist interface {
	Encode() *bytes.Buffer
	Decode(bytes.Buffer, bool) error
	DecodeFrom(reader io.Reader, strict bool) error
	String() string
}

// Internal structure for decoding a line of input stream with a list type detection
type decodingState struct {
	listType           ListType
	m3u                bool
	tagWV              bool
	tagStreamInf       bool
	tagInf             bool
	tagSCTE35          bool
	tagRange           bool
	tagDiscontinuity   bool
	tagProgramDateTime bool
	tagKey             bool
	tagMap             bool
	programDateTime    time.Time
	limit              int64
	offset             int64
	duration           float64
	title              string
	variant            *Variant
	alternatives       []*Alternative
	xkey               *Key
	xmap               *Map
	scte               *SCTE
}
<commit_msg>Update structure.go<commit_after>package m3u8

/*
 Part of M3U8 parser & generator library.
 This file defines data structures related to package.

 Copyright 2013-2017 The Project Developers.
 See the AUTHORS and LICENSE files at the top-level directory of this distribution
 and at https://github.com/grafov/m3u8/

 ॐ तारे तुत्तारे तुरे स्व
*/

import (
	"bytes"
	"io"
	"time"
)

const (
	/*
		Compatibility rules described in section 7:
		Clients and servers MUST implement protocol version 2 or higher to use:
		   o  The IV attribute of the EXT-X-KEY tag.
		   Clients and servers MUST implement protocol version 3 or higher to use:
		   o  Floating-point EXTINF duration values.
		   Clients and servers MUST implement protocol version 4 or higher to use:
		   o  The EXT-X-BYTERANGE tag.
		   o  The EXT-X-I-FRAME-STREAM-INF tag.
		   o  The EXT-X-I-FRAMES-ONLY tag.
		   o  The EXT-X-MEDIA tag.
		   o  The AUDIO and VIDEO attributes of the EXT-X-STREAM-INF tag.
	*/
	minver   = uint8(3)
	DATETIME = time.RFC3339Nano // Format for EXT-X-PROGRAM-DATE-TIME defined in section 3.4.5
)

type ListType uint

const (
	// use 0 for not defined type
	MASTER ListType = iota + 1
	MEDIA
)

// for EXT-X-PLAYLIST-TYPE tag
type MediaType uint

const (
	// use 0 for not defined type
	EVENT MediaType = iota + 1
	VOD
)

// SCTE35Syntax defines the format of the SCTE-35 cue points which do not use
// the draft-pantos-http-live-streaming-19 EXT-X-DATERANGE tag and instead
// have their own custom tags
type SCTE35Syntax uint

const (
	// SCTE35_67_2014 will be the default due to backwards compatibility reasons.
	SCTE35_67_2014 SCTE35Syntax = iota // SCTE35_67_2014 defined in http://www.scte.org/documents/pdf/standards/SCTE%2067%202014.pdf
	SCTE35_OATCLS                      // SCTE35_OATCLS is a non-standard but common format
)

// SCTE35CueType defines the type of cue point, used by readers and writers to
// write a different syntax
type SCTE35CueType uint

const (
	SCTE35Cue_Start SCTE35CueType = iota // SCTE35Cue_Start indicates an out cue point
	SCTE35Cue_Mid                        // SCTE35Cue_Mid indicates a segment between start and end cue points
	SCTE35Cue_End                        // SCTE35Cue_End indicates an in cue point
)

/*
 This structure represents a single bitrate playlist aka media playlist.
 It related to both a simple media playlists and a sliding window media playlists.
 URI lines in the Playlist point to media segments.

 Simple Media Playlist file sample:

   #EXTM3U
   #EXT-X-VERSION:3
   #EXT-X-TARGETDURATION:5220
   #EXTINF:5219.2,
   http://media.example.com/entire.ts
   #EXT-X-ENDLIST

 Sample of Sliding Window Media Playlist, using HTTPS:

   #EXTM3U
   #EXT-X-VERSION:3
   #EXT-X-TARGETDURATION:8
   #EXT-X-MEDIA-SEQUENCE:2680

   #EXTINF:7.975,
   https://priv.example.com/fileSequence2680.ts
   #EXTINF:7.941,
   https://priv.example.com/fileSequence2681.ts
   #EXTINF:7.975,
   https://priv.example.com/fileSequence2682.ts
*/
type MediaPlaylist struct {
	TargetDuration   float64
	SeqNo            uint64 // EXT-X-MEDIA-SEQUENCE
	Segments         []*MediaSegment
	Args             string // optional arguments placed after URIs (URI?Args)
	Iframe           bool   // EXT-X-I-FRAMES-ONLY
	Closed           bool   // is this VOD (closed) or Live (sliding) playlist?
	MediaType        MediaType
	DiscontinuitySeq uint64 // EXT-X-DISCONTINUITY-SEQUENCE
	durationAsInt    bool   // output durations as integers of floats?
	keyformat        int
	winsize          uint // max number of segments displayed in an encoded playlist; need set to zero for VOD playlists
	capacity         uint // total capacity of slice used for the playlist
	head             uint // head of FIFO, we add segments to head
	tail             uint // tail of FIFO, we remove segments from tail
	count            uint // number of segments added to the playlist
	buf              bytes.Buffer
	ver              uint8
	Key              *Key // EXT-X-KEY is optional encryption key displayed before any segments (default key for the playlist)
	Map              *Map // EXT-X-MAP is optional tag specifies how to obtain the Media Initialization Section (default map for the playlist)
	WV               *WV  // Widevine related tags outside of M3U8 specs
}

/*
 This structure represents a master playlist which combines media playlists for multiple bitrates.
 URI lines in the playlist identify media playlists.
 Sample of Master Playlist file:

   #EXTM3U
   #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1280000
   http://example.com/low.m3u8
   #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=2560000
   http://example.com/mid.m3u8
   #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=7680000
   http://example.com/hi.m3u8
   #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=65000,CODECS="mp4a.40.5"
   http://example.com/audio-only.m3u8
*/
type MasterPlaylist struct {
	Variants      []*Variant
	Args          string // optional arguments placed after URI (URI?Args)
	CypherVersion string // non-standard tag for Widevine (see also WV struct)
	buf           bytes.Buffer
	ver           uint8
}

// This structure represents variants for master playlist.
// Variants included in a master playlist and point to media playlists.
type Variant struct {
	URI       string
	Chunklist *MediaPlaylist
	VariantParams
}

// This structure represents additional parameters for a variant
// used in EXT-X-STREAM-INF and EXT-X-I-FRAME-STREAM-INF
type VariantParams struct {
	ProgramId        uint32
	Bandwidth        uint32
	AverageBandwidth uint32 // EXT-X-STREAM-INF only
	Codecs           string
	Resolution       string
	Audio            string // EXT-X-STREAM-INF only
	Video            string
	Subtitles        string         // EXT-X-STREAM-INF only
	Captions         string         // EXT-X-STREAM-INF only
	Name             string         // EXT-X-STREAM-INF only (non standard Wowza/JWPlayer extension to name the variant/quality in UA)
	FrameRate        float64        // EXT-X-STREAM-INF
	Iframe           bool           // EXT-X-I-FRAME-STREAM-INF
	Alternatives     []*Alternative // EXT-X-MEDIA
}

// This structure represents EXT-X-MEDIA tag in variants.
type Alternative struct {
	GroupId         string
	URI             string
	Type            string
	Language        string
	Name            string
	Default         bool
	Autoselect      string
	Forced          string
	Characteristics string
	Subtitles       string
}

// This structure represents a media segment included in a media playlist.
// Media segment may be encrypted.
// Widevine supports own tags for encryption metadata.
type MediaSegment struct {
	SeqId           uint64
	Title           string // optional second parameter for EXTINF tag
	URI             string
	Duration        float64   // first parameter for EXTINF tag; duration must be integers if protocol version is less than 3 but we are always keep them float
	Limit           int64     // EXT-X-BYTERANGE <n> is length in bytes for the file under URI
	Offset          int64     // EXT-X-BYTERANGE [@o] is offset from the start of the file under URI
	Key             *Key      // EXT-X-KEY displayed before the segment and means changing of encryption key (in theory each segment may have own key)
	Map             *Map      // EXT-X-MAP displayed before the segment
	Discontinuity   bool      // EXT-X-DISCONTINUITY indicates an encoding discontinuity between the media segment that follows it and the one that preceded it (i.e. file format, number and type of tracks, encoding parameters, encoding sequence, timestamp sequence)
	SCTE            *SCTE     // SCTE-35 used for Ad signaling in HLS
	ProgramDateTime time.Time // EXT-X-PROGRAM-DATE-TIME tag associates the first sample of a media segment with an absolute date and/or time
}

// SCTE holds custom, non EXT-X-DATERANGE, SCTE-35 tags
type SCTE struct {
	Syntax  SCTE35Syntax  // Syntax defines the format of the SCTE-35 cue tag
	CueType SCTE35CueType // CueType defines whether the cue is a start, mid, end (if applicable)
	Cue     string
	ID      string
	Time    float64
	Elapsed float64
}

// This structure represents information about stream encryption.
//
// Realizes EXT-X-KEY tag.
type Key struct {
	Method            string
	URI               string
	IV                string
	Keyformat         string
	Keyformatversions string
}

// This structure represents specifies how to obtain the Media
// Initialization Section required to parse the applicable
// Media Segments.

// It applies to every Media Segment that appears after it in the
// Playlist until the next EXT-X-MAP tag or until the end of the
// playlist.
//
// Realizes EXT-MAP tag.
type Map struct {
	URI    string
	Limit  int64 // <n> is length in bytes for the file under URI
	Offset int64 // [@o] is offset from the start of the file under URI
}

// This structure represents metadata  for Google Widevine playlists.
// This format not described in IETF draft but provied by Widevine Live Packager as
// additional tags with #WV-prefix.
type WV struct {
	AudioChannels          uint
	AudioFormat            uint
	AudioProfileIDC        uint
	AudioSampleSize        uint
	AudioSamplingFrequency uint
	CypherVersion          string
	ECM                    string
	VideoFormat            uint
	VideoFrameRate         uint
	VideoLevelIDC          uint
	VideoProfileIDC        uint
	VideoResolution        string
	VideoSAR               string
}

// Interface applied to various playlist types.
type Playlist interface {
	Encode() *bytes.Buffer
	Decode(bytes.Buffer, bool) error
	DecodeFrom(reader io.Reader, strict bool) error
	String() string
}

// Internal structure for decoding a line of input stream with a list type detection
type decodingState struct {
	listType           ListType
	m3u                bool
	tagWV              bool
	tagStreamInf       bool
	tagInf             bool
	tagSCTE35          bool
	tagRange           bool
	tagDiscontinuity   bool
	tagProgramDateTime bool
	tagKey             bool
	tagMap             bool
	programDateTime    time.Time
	limit              int64
	offset             int64
	duration           float64
	title              string
	variant            *Variant
	alternatives       []*Alternative
	xkey               *Key
	xmap               *Map
	scte               *SCTE
}
<|endoftext|>


<commit_before>package stripe

// ReportRunStatus is the possible values for status on a report run.
type ReportRunStatus string

// List of values that ReportRunStatus can take.
const (
	ReportRunStatusFailed    ReportRunStatus = "failed"
	ReportRunStatusPending   ReportRunStatus = "pending"
	ReportRunStatusSucceeded ReportRunStatus = "succeeded"
)

// ReportRunParametersParams is the set of parameters that can be used when creating a report run.
type ReportRunParametersParams struct {
	Columns           []*string `form:"columns"`
	ConnectedAccount  *string   `form:"connected_account"`
	Currency          *string   `form:"currency"`
	IntervalEnd       *int64    `form:"interval_end"`
	IntervalStart     *int64    `form:"interval_start"`
	Payout            *string   `form:"payout"`
	ReportingCategory *string   `form:"reporting_category"`
}

// ReportRunParams is the set of parameters that can be used when creating a report run.
type ReportRunParams struct {
	Params     `form:"*"`
	Parameters *ReportRunParametersParams `form:"parameters"`
	ReportType *string                    `form:"report_type"`
}

// ReportRunListParams is the set of parameters that can be used when listing report runs.
type ReportRunListParams struct {
	ListParams   `form:"*"`
	Created      *int64            `form:"created"`
	CreatedRange *RangeQueryParams `form:"created"`
}

// ReportRunParameters describes the parameters hash on a report run.
type ReportRunParameters struct {
	Columns           []string `json:"columns"`
	ConnectedAccount  string   `json:"connected_account"`
	Currency          Currency `json:"currency"`
	IntervalEnd       int64    `json:"interval_end"`
	IntervalStart     int64    `json:"interval_start"`
	Payout            string   `json:"payout"`
	ReportingCategory string   `json:"reporting_category"`
}

// ReportRun is the resource representing a report run.
type ReportRun struct {
	Created     int64                `json:"created"`
	Error       string               `json:"error"`
	ID          string               `json:"id"`
	Livemode    bool                 `json:"livemode"`
	Object      string               `json:"object"`
	Parameters  *ReportRunParameters `json:"parameters"`
	ReportType  string               `json:"report_type"`
	Result      *File                `json:"result"`
	Status      ReportRunStatus      `json:"status"`
	SucceededAt int64                `json:"succeeded_at"`
}

// ReportRunList is a list of report runs as retrieved from a list endpoint.
type ReportRunList struct {
	ListMeta
	Data []*ReportRun `json:"data"`
}
<commit_msg>Add support for `Timezone` on `ReportRun`<commit_after>package stripe

// ReportRunStatus is the possible values for status on a report run.
type ReportRunStatus string

// List of values that ReportRunStatus can take.
const (
	ReportRunStatusFailed    ReportRunStatus = "failed"
	ReportRunStatusPending   ReportRunStatus = "pending"
	ReportRunStatusSucceeded ReportRunStatus = "succeeded"
)

// ReportRunParametersParams is the set of parameters that can be used when creating a report run.
type ReportRunParametersParams struct {
	Columns           []*string `form:"columns"`
	ConnectedAccount  *string   `form:"connected_account"`
	Currency          *string   `form:"currency"`
	IntervalEnd       *int64    `form:"interval_end"`
	IntervalStart     *int64    `form:"interval_start"`
	Payout            *string   `form:"payout"`
	ReportingCategory *string   `form:"reporting_category"`
	Timezone          *string   `form:"timezone"`
}

// ReportRunParams is the set of parameters that can be used when creating a report run.
type ReportRunParams struct {
	Params     `form:"*"`
	Parameters *ReportRunParametersParams `form:"parameters"`
	ReportType *string                    `form:"report_type"`
}

// ReportRunListParams is the set of parameters that can be used when listing report runs.
type ReportRunListParams struct {
	ListParams   `form:"*"`
	Created      *int64            `form:"created"`
	CreatedRange *RangeQueryParams `form:"created"`
}

// ReportRunParameters describes the parameters hash on a report run.
type ReportRunParameters struct {
	Columns           []string `json:"columns"`
	ConnectedAccount  string   `json:"connected_account"`
	Currency          Currency `json:"currency"`
	IntervalEnd       int64    `json:"interval_end"`
	IntervalStart     int64    `json:"interval_start"`
	Payout            string   `json:"payout"`
	ReportingCategory string   `json:"reporting_category"`
	Timezone          string   `json:"timezone"`
}

// ReportRun is the resource representing a report run.
type ReportRun struct {
	Created     int64                `json:"created"`
	Error       string               `json:"error"`
	ID          string               `json:"id"`
	Livemode    bool                 `json:"livemode"`
	Object      string               `json:"object"`
	Parameters  *ReportRunParameters `json:"parameters"`
	ReportType  string               `json:"report_type"`
	Result      *File                `json:"result"`
	Status      ReportRunStatus      `json:"status"`
	SucceededAt int64                `json:"succeeded_at"`
}

// ReportRunList is a list of report runs as retrieved from a list endpoint.
type ReportRunList struct {
	ListMeta
	Data []*ReportRun `json:"data"`
}
<|endoftext|>


<commit_before>package doorman

import (
	"bytes"
	"encoding/json"
	"io"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"os"
	"testing"

	"github.com/gin-gonic/gin"
	"github.com/ory/ladon"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

type Policy struct {
	ID          string
	Description string
}

type User struct {
	ID string
}

type Response struct {
	Allowed bool
	User    User
	Policy  Policy
}

type ErrorResponse struct {
	Message string
}

func TestMain(m *testing.M) {
	//Set Gin to Test Mode
	gin.SetMode(gin.TestMode)
	// Run the other tests
	os.Exit(m.Run())
}

func loadTempFiles(contents ...string) error {
	var filenames []string
	for _, content := range contents {
		tmpfile, _ := ioutil.TempFile("", "")
		defer os.Remove(tmpfile.Name()) // clean up
		tmpfile.Write([]byte(content))
		tmpfile.Close()
		filenames = append(filenames, tmpfile.Name())
	}
	_, err := New(filenames, "")
	return err
}

func TestLoadBadPolicies(t *testing.T) {
	// Loads policies.yaml in current folder by default.
	_, err := New([]string{}, "")
	assert.NotNil(t, err) // doorman/policies.yaml does not exists.

	// Missing file
	_, err = New([]string{"/tmp/unknown.yaml"}, "")
	assert.NotNil(t, err)

	// Empty file
	err = loadTempFiles("")
	assert.NotNil(t, err)

	// Bad YAML
	err = loadTempFiles("$\\--xx")
	assert.NotNil(t, err)

	// Empty audience
	err = loadTempFiles(`
audience:
policies:
  -
    id: "1"
    effect: allow
`)
	assert.NotNil(t, err)

	// Bad audience
	err = loadTempFiles(`
audience: 1
policies:
  -
    id: "1"
    effect: allow
`)
	assert.NotNil(t, err)

	// Bad policies conditions
	err = loadTempFiles(`
audience: a
policies:
  -
    id: "1"
    conditions:
      - a
      - b
`)
	assert.NotNil(t, err)

	// Duplicated policy ID
	err = loadTempFiles(`
audience: a
policies:
  -
    id: "1"
    effect: allow
  -
    id: "1"
    effect: deny
`)
	assert.NotNil(t, err)

	// Duplicated audience
	err = loadTempFiles(`
audience: a
policies:
  -
    id: "1"
    effect: allow
`, `
audience: a
policies:
  -
    id: "1"
    effect: allow
`)
	assert.NotNil(t, err)
}

func TestReloadPolicies(t *testing.T) {
	doorman, err := New([]string{"../sample.yaml"}, "")
	assert.Nil(t, err)
	loaded, _ := doorman.ladons["https://sample.yaml"].Manager.GetAll(0, maxInt)
	assert.Equal(t, 5, len(loaded))

	// Second load.
	doorman.loadPolicies()
	loaded, _ = doorman.ladons["https://sample.yaml"].Manager.GetAll(0, maxInt)
	assert.Equal(t, 5, len(loaded))
}

func TestIsAllowed(t *testing.T) {
	doorman, err := New([]string{"../sample.yaml"}, "")
	assert.Nil(t, err)

	request := &ladon.Request{
		// Policy #1
		Subject:  "foo",
		Action:   "update",
		Resource: "server.org/blocklist:onecrl",
	}
	assert.Nil(t, doorman.IsAllowed("https://sample.yaml", request))
	assert.NotNil(t, doorman.IsAllowed("https://bad.audience", request))
}

func performRequest(r http.Handler, method, path string, body io.Reader) *httptest.ResponseRecorder {
	req, _ := http.NewRequest(method, path, body)
	req.Header.Set("Origin", "https://sample.yaml")
	req.Header.Set("Content-Type", "application/json")
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)
	return w
}

func performAllowed(t *testing.T, r *gin.Engine, body io.Reader, expected int, response interface{}) {
	w := performRequest(r, "POST", "/allowed", body)
	require.Equal(t, expected, w.Code)
	err := json.Unmarshal(w.Body.Bytes(), &response)
	require.Nil(t, err)
}

func TestDoormanGet(t *testing.T) {
	r := gin.New()
	doorman, _ := New([]string{"../sample.yaml"}, "")
	SetupRoutes(r, doorman)

	w := performRequest(r, "GET", "/allowed", nil)
	assert.Equal(t, w.Code, http.StatusNotFound)
}

func TestDoormanEmpty(t *testing.T) {
	r := gin.New()
	doorman, _ := New([]string{"../sample.yaml"}, "")
	SetupRoutes(r, doorman)

	var response ErrorResponse
	performAllowed(t, r, nil, http.StatusBadRequest, &response)
	assert.Equal(t, response.Message, "Missing body")
}

func TestDoormanInvalidJSON(t *testing.T) {
	r := gin.New()
	doorman, _ := New([]string{"../sample.yaml"}, "")
	SetupRoutes(r, doorman)

	body := bytes.NewBuffer([]byte("{\"random\\;mess\"}"))
	var response ErrorResponse
	performAllowed(t, r, body, http.StatusBadRequest, &response)
	assert.Contains(t, response.Message, "invalid character ';'")
}

func TestDoormanAllowed(t *testing.T) {
	r := gin.New()
	doorman, _ := New([]string{"../sample.yaml"}, "")
	SetupRoutes(r, doorman)

	for _, request := range []*ladon.Request{
		// Policy #1
		{
			Subject:  "foo",
			Action:   "update",
			Resource: "server.org/blocklist:onecrl",
		},
		// Policy #2
		{
			Subject:  "foo",
			Action:   "update",
			Resource: "server.org/blocklist:onecrl",
			Context: ladon.Context{
				"planet": "Mars", // "mars" is case-sensitive
			},
		},
		// Policy #3
		{
			Subject:  "foo",
			Action:   "read",
			Resource: "server.org/blocklist:onecrl",
			Context: ladon.Context{
				"ip": "127.0.0.1",
			},
		},
		// Policy #4
		{
			Subject:  "bilbo",
			Action:   "wear",
			Resource: "ring",
			Context: ladon.Context{
				"owner": "bilbo",
			},
		},
		// Policy #5
		{
			Subject:  "group:admins",
			Action:   "create",
			Resource: "dns://",
			Context: ladon.Context{
				"domain": "kinto.mozilla.org",
			},
		},
	} {
		token, _ := json.Marshal(request)
		body := bytes.NewBuffer(token)
		var response Response
		performAllowed(t, r, body, http.StatusOK, &response)
		assert.Equal(t, true, response.Allowed)
	}
}

func TestDoormanNotAllowed(t *testing.T) {
	r := gin.New()
	doorman, _ := New([]string{"../sample.yaml"}, "")
	SetupRoutes(r, doorman)

	for _, request := range []*ladon.Request{
		// Policy #1
		{
			Subject:  "foo",
			Action:   "delete",
			Resource: "server.org/blocklist:onecrl",
		},
		// Policy #2
		{
			Subject:  "foo",
			Action:   "update",
			Resource: "server.org/blocklist:onecrl",
			Context: ladon.Context{
				"planet": "mars",
			},
		},
		// Policy #3
		{
			Subject:  "foo",
			Action:   "read",
			Resource: "server.org/blocklist:onecrl",
			Context: ladon.Context{
				"ip": "10.0.0.1",
			},
		},
		// Policy #4
		{
			Subject:  "gollum",
			Action:   "wear",
			Resource: "ring",
			Context: ladon.Context{
				"owner": "bilbo",
			},
		},
		// Policy #5
		{
			Subject:  "group:admins",
			Action:   "create",
			Resource: "dns://",
			Context: ladon.Context{
				"domain": "kinto-storage.org",
			},
		},
		// Default
		{},
	} {
		token, _ := json.Marshal(request)
		body := bytes.NewBuffer(token)
		var response Response
		performAllowed(t, r, body, http.StatusOK, &response)
		assert.Equal(t, false, response.Allowed)
	}
}

func TestDoormanVerifiesJWT(t *testing.T) {
	r := gin.New()
	doorman, _ := New([]string{"../sample.yaml"}, "https://auth.mozilla.auth0.com/")
	SetupRoutes(r, doorman)

	// Policy #1 will match.
	request := ladon.Request{
		Subject:  "foo",
		Action:   "delete",
		Resource: "server.org/blocklist:onecrl",
	}
	token, _ := json.Marshal(request)
	body := bytes.NewBuffer(token)
	var response ErrorResponse

	// Missing Authorization header.
	performAllowed(t, r, body, http.StatusUnauthorized, &response)
	assert.Equal(t, "Token not found", response.Message)
}
<commit_msg>Add test for empty list of policies<commit_after>package doorman

import (
	"bytes"
	"encoding/json"
	"io"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"os"
	"testing"

	"github.com/gin-gonic/gin"
	"github.com/ory/ladon"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

type Policy struct {
	ID          string
	Description string
}

type User struct {
	ID string
}

type Response struct {
	Allowed bool
	User    User
	Policy  Policy
}

type ErrorResponse struct {
	Message string
}

func TestMain(m *testing.M) {
	//Set Gin to Test Mode
	gin.SetMode(gin.TestMode)
	// Run the other tests
	os.Exit(m.Run())
}

func loadTempFiles(contents ...string) error {
	var filenames []string
	for _, content := range contents {
		tmpfile, _ := ioutil.TempFile("", "")
		defer os.Remove(tmpfile.Name()) // clean up
		tmpfile.Write([]byte(content))
		tmpfile.Close()
		filenames = append(filenames, tmpfile.Name())
	}
	_, err := New(filenames, "")
	return err
}

func TestLoadBadPolicies(t *testing.T) {
	// Loads policies.yaml in current folder by default.
	_, err := New([]string{}, "")
	assert.NotNil(t, err) // doorman/policies.yaml does not exists.

	// Missing file
	_, err = New([]string{"/tmp/unknown.yaml"}, "")
	assert.NotNil(t, err)

	// Empty file
	err = loadTempFiles("")
	assert.NotNil(t, err)

	// Bad YAML
	err = loadTempFiles("$\\--xx")
	assert.NotNil(t, err)

	// Empty audience
	err = loadTempFiles(`
audience:
policies:
  -
    id: "1"
    effect: allow
`)
	assert.NotNil(t, err)

	// Empty policies
	err = loadTempFiles(`
audience: a
policies:
`)
	assert.Nil(t, err)

	// Bad audience
	err = loadTempFiles(`
audience: 1
policies:
  -
    id: "1"
    effect: allow
`)
	assert.NotNil(t, err)

	// Bad policies conditions
	err = loadTempFiles(`
audience: a
policies:
  -
    id: "1"
    conditions:
      - a
      - b
`)
	assert.NotNil(t, err)

	// Duplicated policy ID
	err = loadTempFiles(`
audience: a
policies:
  -
    id: "1"
    effect: allow
  -
    id: "1"
    effect: deny
`)
	assert.NotNil(t, err)

	// Duplicated audience
	err = loadTempFiles(`
audience: a
policies:
  -
    id: "1"
    effect: allow
`, `
audience: a
policies:
  -
    id: "1"
    effect: allow
`)
	assert.NotNil(t, err)
}

func TestReloadPolicies(t *testing.T) {
	doorman, err := New([]string{"../sample.yaml"}, "")
	assert.Nil(t, err)
	loaded, _ := doorman.ladons["https://sample.yaml"].Manager.GetAll(0, maxInt)
	assert.Equal(t, 5, len(loaded))

	// Second load.
	doorman.loadPolicies()
	loaded, _ = doorman.ladons["https://sample.yaml"].Manager.GetAll(0, maxInt)
	assert.Equal(t, 5, len(loaded))
}

func TestIsAllowed(t *testing.T) {
	doorman, err := New([]string{"../sample.yaml"}, "")
	assert.Nil(t, err)

	request := &ladon.Request{
		// Policy #1
		Subject:  "foo",
		Action:   "update",
		Resource: "server.org/blocklist:onecrl",
	}
	assert.Nil(t, doorman.IsAllowed("https://sample.yaml", request))
	assert.NotNil(t, doorman.IsAllowed("https://bad.audience", request))
}

func performRequest(r http.Handler, method, path string, body io.Reader) *httptest.ResponseRecorder {
	req, _ := http.NewRequest(method, path, body)
	req.Header.Set("Origin", "https://sample.yaml")
	req.Header.Set("Content-Type", "application/json")
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)
	return w
}

func performAllowed(t *testing.T, r *gin.Engine, body io.Reader, expected int, response interface{}) {
	w := performRequest(r, "POST", "/allowed", body)
	require.Equal(t, expected, w.Code)
	err := json.Unmarshal(w.Body.Bytes(), &response)
	require.Nil(t, err)
}

func TestDoormanGet(t *testing.T) {
	r := gin.New()
	doorman, _ := New([]string{"../sample.yaml"}, "")
	SetupRoutes(r, doorman)

	w := performRequest(r, "GET", "/allowed", nil)
	assert.Equal(t, w.Code, http.StatusNotFound)
}

func TestDoormanEmpty(t *testing.T) {
	r := gin.New()
	doorman, _ := New([]string{"../sample.yaml"}, "")
	SetupRoutes(r, doorman)

	var response ErrorResponse
	performAllowed(t, r, nil, http.StatusBadRequest, &response)
	assert.Equal(t, response.Message, "Missing body")
}

func TestDoormanInvalidJSON(t *testing.T) {
	r := gin.New()
	doorman, _ := New([]string{"../sample.yaml"}, "")
	SetupRoutes(r, doorman)

	body := bytes.NewBuffer([]byte("{\"random\\;mess\"}"))
	var response ErrorResponse
	performAllowed(t, r, body, http.StatusBadRequest, &response)
	assert.Contains(t, response.Message, "invalid character ';'")
}

func TestDoormanAllowed(t *testing.T) {
	r := gin.New()
	doorman, _ := New([]string{"../sample.yaml"}, "")
	SetupRoutes(r, doorman)

	for _, request := range []*ladon.Request{
		// Policy #1
		{
			Subject:  "foo",
			Action:   "update",
			Resource: "server.org/blocklist:onecrl",
		},
		// Policy #2
		{
			Subject:  "foo",
			Action:   "update",
			Resource: "server.org/blocklist:onecrl",
			Context: ladon.Context{
				"planet": "Mars", // "mars" is case-sensitive
			},
		},
		// Policy #3
		{
			Subject:  "foo",
			Action:   "read",
			Resource: "server.org/blocklist:onecrl",
			Context: ladon.Context{
				"ip": "127.0.0.1",
			},
		},
		// Policy #4
		{
			Subject:  "bilbo",
			Action:   "wear",
			Resource: "ring",
			Context: ladon.Context{
				"owner": "bilbo",
			},
		},
		// Policy #5
		{
			Subject:  "group:admins",
			Action:   "create",
			Resource: "dns://",
			Context: ladon.Context{
				"domain": "kinto.mozilla.org",
			},
		},
	} {
		token, _ := json.Marshal(request)
		body := bytes.NewBuffer(token)
		var response Response
		performAllowed(t, r, body, http.StatusOK, &response)
		assert.Equal(t, true, response.Allowed)
	}
}

func TestDoormanNotAllowed(t *testing.T) {
	r := gin.New()
	doorman, _ := New([]string{"../sample.yaml"}, "")
	SetupRoutes(r, doorman)

	for _, request := range []*ladon.Request{
		// Policy #1
		{
			Subject:  "foo",
			Action:   "delete",
			Resource: "server.org/blocklist:onecrl",
		},
		// Policy #2
		{
			Subject:  "foo",
			Action:   "update",
			Resource: "server.org/blocklist:onecrl",
			Context: ladon.Context{
				"planet": "mars",
			},
		},
		// Policy #3
		{
			Subject:  "foo",
			Action:   "read",
			Resource: "server.org/blocklist:onecrl",
			Context: ladon.Context{
				"ip": "10.0.0.1",
			},
		},
		// Policy #4
		{
			Subject:  "gollum",
			Action:   "wear",
			Resource: "ring",
			Context: ladon.Context{
				"owner": "bilbo",
			},
		},
		// Policy #5
		{
			Subject:  "group:admins",
			Action:   "create",
			Resource: "dns://",
			Context: ladon.Context{
				"domain": "kinto-storage.org",
			},
		},
		// Default
		{},
	} {
		token, _ := json.Marshal(request)
		body := bytes.NewBuffer(token)
		var response Response
		performAllowed(t, r, body, http.StatusOK, &response)
		assert.Equal(t, false, response.Allowed)
	}
}

func TestDoormanVerifiesJWT(t *testing.T) {
	r := gin.New()
	doorman, _ := New([]string{"../sample.yaml"}, "https://auth.mozilla.auth0.com/")
	SetupRoutes(r, doorman)

	// Policy #1 will match.
	request := ladon.Request{
		Subject:  "foo",
		Action:   "delete",
		Resource: "server.org/blocklist:onecrl",
	}
	token, _ := json.Marshal(request)
	body := bytes.NewBuffer(token)
	var response ErrorResponse

	// Missing Authorization header.
	performAllowed(t, r, body, http.StatusUnauthorized, &response)
	assert.Equal(t, "Token not found", response.Message)
}
<|endoftext|>


<commit_before>package fipple

import (
	"bytes"
	"io"
	"mime/multipart"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"os"
	"strings"
	"testing"
)

// Recorder may be used to record http responses
type Recorder struct {
	t       *testing.T
	client  *http.Client
	baseURL string
}

// NewRecorder creates a new recorder with the given baseURL.
// t will be used to print out helpful error messages if any
// assertions fail.
func NewRecorder(t *testing.T, baseURL string) *Recorder {
	jar, err := cookiejar.New(nil)
	if err != nil {
		t.Fatal(err)
	}
	return &Recorder{
		t:       t,
		client:  &http.Client{Jar: jar},
		baseURL: baseURL,
	}
}

func (r *Recorder) newResponse(resp *http.Response) *Response {
	return &Response{
		Response: resp,
		recorder: r,
	}
}

// NewRequest creates a new request object with the given http
// method and path. The path will be appended to the baseURL
// for the recorder to create the full URL. You are free to
// add additional parameters or headers to the request before
// sending it. Any errors that occur will be passed to t.Fatal.
func (r *Recorder) NewRequest(method string, path string) *http.Request {
	fullURL := r.baseURL + path
	req, err := http.NewRequest(method, fullURL, nil)
	if err != nil {
		r.t.Fatal(err)
	}
	return req
}

// NewRequestWithData can be used to easily send a request with
// form data (encoded as application/x-www-form-urlencoded). The
// path will be appended to the baseURL for the recorder to create
// the full URL. Any errors tha occur will be passed to t.Fatal.
func (r *Recorder) NewRequestWithData(method string, path string, data map[string]string) *http.Request {
	fullURL := r.baseURL + path
	v := url.Values{}
	for key, value := range data {
		v.Add(key, value)
	}
	req, err := http.NewRequest(method, fullURL, strings.NewReader(v.Encode()))
	if err != nil {
		r.t.Fatal(err)
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	return req
}

// NewMultipartRequest can be used to easily create (and later send)
// a request with form data and/or files (encoded as multipart/form-data).
// fields is a key-value map of basic string fields for the form data, and
// files is a map of key to *fipple.File
func (r *Recorder) NewMultipartRequest(method string, path string, fields map[string]string, files map[string]*os.File) *http.Request {
	fullURL := r.baseURL + path

	// First, create a new multipart form writer.
	body := bytes.NewBuffer([]byte{})
	form := multipart.NewWriter(body)

	// Add the key-value field params to the form
	for fieldname, value := range fields {
		if err := form.WriteField(fieldname, value); err != nil {
			r.t.Fatal(err)
		}
	}

	// Add the files to the form
	for fieldname, file := range files {
		fileWriter, err := form.CreateFormFile(fieldname, file.Name())
		if err != nil {
			r.t.Fatal(err)
		}
		if _, err := io.Copy(fileWriter, file); err != nil {
			r.t.Fatal(err)
		}
	}

	// Close the form to finish writing
	if err := form.Close(); err != nil {
		r.t.Fatal(err)
	}

	// Create and return the request object
	req, err := http.NewRequest(method, fullURL, body)
	if err != nil {
		r.t.Fatal(err)
	}
	req.Header.Add("Content-Type", "multipart/form-data; boundary="+form.Boundary())
	return req
}

// Do sends req and records the results into a fipple.Response.
// Note that because an http.Request should have already been created
// with a full, valid url, the baseURL of the Recorder will not be prepended
// to the url for req. You can run methods on the response to check
// the results. Any errors that occur will be passed to t.Fatal
func (r *Recorder) Do(req *http.Request) *Response {
	httpResp, err := r.client.Do(req)
	if err != nil {
		r.t.Fatal(err)
	}
	resp := r.newResponse(httpResp)
	resp.ReadBody()
	return resp
}

// Get sends a GET request to the given path and records the results into
// a fipple.Response. path will be appended to the baseURL for the recorder
// to create the full URL. You can run methods on the response to check the
// results. Any errors that occur will be passed to t.Fatal
func (r *Recorder) Get(path string) *Response {
	req := r.NewRequest("GET", path)
	return r.Do(req)
}

// Post sends a POST request to the given path using the given data as post
// parameters and records the results into a fipple.Response. path will be
// appended to the baseURL for the recorder to create the full URL. You
// can run methods on the response to check the results. Any errors that occur
// will be passed to t.Fatal
func (r *Recorder) Post(path string, data map[string]string) *Response {
	req := r.NewRequestWithData("POST", path, data)
	return r.Do(req)
}

// Put sends a PUT request to the given path using the given data as
// parameters and records the results into a fipple.Response. path
// will be appended to the baseURL for the recorder to create the
// full URL. You can run methods on the response to check the results.
// Any errors that occur will be passed to t.Fatal
func (r *Recorder) Put(path string, data map[string]string) *Response {
	req := r.NewRequestWithData("PUT", path, data)
	return r.Do(req)
}

// Delete sends a DELETE request to the given path and records the results
// into a fipple.Response. path will be appended to the baseURL for the recorder
// to create the full URL. You can run methods on the response to check the
// results. Any errors that occur will be passed to t.Fatal
func (r *Recorder) Delete(path string) *Response {
	req := r.NewRequest("DELETE", path)
	return r.Do(req)
}

// GetCookies returns the raw cookies that have been set as a result
// of any requests recorded by a Recorder. Any errors that occur will be
// passed to t.Fatal
func (r *Recorder) GetCookies() []*http.Cookie {
	fullURL, err := url.Parse(r.baseURL)
	if err != nil {
		r.t.Fatal(err)
	}
	return r.client.Jar.Cookies(fullURL)
}
<commit_msg>Add package comment<commit_after>// Package fipple is a testing utility which lets you easily record and test http responses from any url.
// It works great for integration testing REST APIs.
package fipple

import (
	"bytes"
	"io"
	"mime/multipart"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"os"
	"strings"
	"testing"
)

// Recorder may be used to record http responses
type Recorder struct {
	t       *testing.T
	client  *http.Client
	baseURL string
}

// NewRecorder creates a new recorder with the given baseURL.
// t will be used to print out helpful error messages if any
// assertions fail.
func NewRecorder(t *testing.T, baseURL string) *Recorder {
	jar, err := cookiejar.New(nil)
	if err != nil {
		t.Fatal(err)
	}
	return &Recorder{
		t:       t,
		client:  &http.Client{Jar: jar},
		baseURL: baseURL,
	}
}

func (r *Recorder) newResponse(resp *http.Response) *Response {
	return &Response{
		Response: resp,
		recorder: r,
	}
}

// NewRequest creates a new request object with the given http
// method and path. The path will be appended to the baseURL
// for the recorder to create the full URL. You are free to
// add additional parameters or headers to the request before
// sending it. Any errors that occur will be passed to t.Fatal.
func (r *Recorder) NewRequest(method string, path string) *http.Request {
	fullURL := r.baseURL + path
	req, err := http.NewRequest(method, fullURL, nil)
	if err != nil {
		r.t.Fatal(err)
	}
	return req
}

// NewRequestWithData can be used to easily send a request with
// form data (encoded as application/x-www-form-urlencoded). The
// path will be appended to the baseURL for the recorder to create
// the full URL. Any errors tha occur will be passed to t.Fatal.
func (r *Recorder) NewRequestWithData(method string, path string, data map[string]string) *http.Request {
	fullURL := r.baseURL + path
	v := url.Values{}
	for key, value := range data {
		v.Add(key, value)
	}
	req, err := http.NewRequest(method, fullURL, strings.NewReader(v.Encode()))
	if err != nil {
		r.t.Fatal(err)
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	return req
}

// NewMultipartRequest can be used to easily create (and later send)
// a request with form data and/or files (encoded as multipart/form-data).
// fields is a key-value map of basic string fields for the form data, and
// files is a map of key to *fipple.File
func (r *Recorder) NewMultipartRequest(method string, path string, fields map[string]string, files map[string]*os.File) *http.Request {
	fullURL := r.baseURL + path

	// First, create a new multipart form writer.
	body := bytes.NewBuffer([]byte{})
	form := multipart.NewWriter(body)

	// Add the key-value field params to the form
	for fieldname, value := range fields {
		if err := form.WriteField(fieldname, value); err != nil {
			r.t.Fatal(err)
		}
	}

	// Add the files to the form
	for fieldname, file := range files {
		fileWriter, err := form.CreateFormFile(fieldname, file.Name())
		if err != nil {
			r.t.Fatal(err)
		}
		if _, err := io.Copy(fileWriter, file); err != nil {
			r.t.Fatal(err)
		}
	}

	// Close the form to finish writing
	if err := form.Close(); err != nil {
		r.t.Fatal(err)
	}

	// Create and return the request object
	req, err := http.NewRequest(method, fullURL, body)
	if err != nil {
		r.t.Fatal(err)
	}
	req.Header.Add("Content-Type", "multipart/form-data; boundary="+form.Boundary())
	return req
}

// Do sends req and records the results into a fipple.Response.
// Note that because an http.Request should have already been created
// with a full, valid url, the baseURL of the Recorder will not be prepended
// to the url for req. You can run methods on the response to check
// the results. Any errors that occur will be passed to t.Fatal
func (r *Recorder) Do(req *http.Request) *Response {
	httpResp, err := r.client.Do(req)
	if err != nil {
		r.t.Fatal(err)
	}
	resp := r.newResponse(httpResp)
	resp.ReadBody()
	return resp
}

// Get sends a GET request to the given path and records the results into
// a fipple.Response. path will be appended to the baseURL for the recorder
// to create the full URL. You can run methods on the response to check the
// results. Any errors that occur will be passed to t.Fatal
func (r *Recorder) Get(path string) *Response {
	req := r.NewRequest("GET", path)
	return r.Do(req)
}

// Post sends a POST request to the given path using the given data as post
// parameters and records the results into a fipple.Response. path will be
// appended to the baseURL for the recorder to create the full URL. You
// can run methods on the response to check the results. Any errors that occur
// will be passed to t.Fatal
func (r *Recorder) Post(path string, data map[string]string) *Response {
	req := r.NewRequestWithData("POST", path, data)
	return r.Do(req)
}

// Put sends a PUT request to the given path using the given data as
// parameters and records the results into a fipple.Response. path
// will be appended to the baseURL for the recorder to create the
// full URL. You can run methods on the response to check the results.
// Any errors that occur will be passed to t.Fatal
func (r *Recorder) Put(path string, data map[string]string) *Response {
	req := r.NewRequestWithData("PUT", path, data)
	return r.Do(req)
}

// Delete sends a DELETE request to the given path and records the results
// into a fipple.Response. path will be appended to the baseURL for the recorder
// to create the full URL. You can run methods on the response to check the
// results. Any errors that occur will be passed to t.Fatal
func (r *Recorder) Delete(path string) *Response {
	req := r.NewRequest("DELETE", path)
	return r.Do(req)
}

// GetCookies returns the raw cookies that have been set as a result
// of any requests recorded by a Recorder. Any errors that occur will be
// passed to t.Fatal
func (r *Recorder) GetCookies() []*http.Cookie {
	fullURL, err := url.Parse(r.baseURL)
	if err != nil {
		r.t.Fatal(err)
	}
	return r.client.Jar.Cookies(fullURL)
}
<|endoftext|>


<commit_before>package reflect2

import (
	"github.com/modern-go/concurrent"
	"reflect"
	"unsafe"
)

type Type interface {
	Kind() reflect.Kind
	// New return pointer to data of this type
	New() interface{}
	// UnsafeNew return the allocated space pointed by unsafe.Pointer
	UnsafeNew() unsafe.Pointer
	// PackEFace cast a unsafe pointer to object represented pointer
	PackEFace(ptr unsafe.Pointer) interface{}
	// Indirect dereference object represented pointer to this type
	Indirect(obj interface{}) interface{}
	// UnsafeIndirect dereference pointer to this type
	UnsafeIndirect(ptr unsafe.Pointer) interface{}
	// Type1 returns reflect.Type
	Type1() reflect.Type
	Implements(thatType Type) bool
	String() string
	RType() uintptr
	// interface{} of this type has pointer like behavior
	LikePtr() bool
	IsNullable() bool
	IsNil(obj interface{}) bool
	UnsafeIsNil(ptr unsafe.Pointer) bool
	Set(obj interface{}, val interface{})
	UnsafeSet(ptr unsafe.Pointer, val unsafe.Pointer)
	AssignableTo(anotherType Type) bool
}

type ListType interface {
	Type
	Elem() Type
	SetIndex(obj interface{}, index int, elem interface{})
	UnsafeSetIndex(obj unsafe.Pointer, index int, elem unsafe.Pointer)
	GetIndex(obj interface{}, index int) interface{}
	UnsafeGetIndex(obj unsafe.Pointer, index int) unsafe.Pointer
}

type ArrayType interface {
	ListType
	Len() int
}

type SliceType interface {
	ListType
	MakeSlice(length int, cap int) interface{}
	UnsafeMakeSlice(length int, cap int) unsafe.Pointer
	Grow(obj interface{}, newLength int)
	UnsafeGrow(ptr unsafe.Pointer, newLength int)
	Append(obj interface{}, elem interface{})
	UnsafeAppend(obj unsafe.Pointer, elem unsafe.Pointer)
	LengthOf(obj interface{}) int
	UnsafeLengthOf(ptr unsafe.Pointer) int
	SetNil(obj interface{})
	UnsafeSetNil(ptr unsafe.Pointer)
	Cap(obj interface{}) int
	UnsafeCap(ptr unsafe.Pointer) int
}

type StructType interface {
	Type
	NumField() int
	Field(i int) StructField
	FieldByName(name string) StructField
	FieldByIndex(index []int) StructField
	FieldByNameFunc(match func(string) bool) StructField
}

type StructField interface {
	Offset() uintptr
	Name() string
	PkgPath() string
	Type() Type
	Tag() reflect.StructTag
	Index() []int
	Anonymous() bool
	Set(obj interface{}, value interface{})
	UnsafeSet(obj unsafe.Pointer, value unsafe.Pointer)
	Get(obj interface{}) interface{}
	UnsafeGet(obj unsafe.Pointer) unsafe.Pointer
}

type MapType interface {
	Type
	Key() Type
	Elem() Type
	MakeMap(cap int) interface{}
	UnsafeMakeMap(cap int) unsafe.Pointer
	SetIndex(obj interface{}, key interface{}, elem interface{})
	UnsafeSetIndex(obj unsafe.Pointer, key unsafe.Pointer, elem unsafe.Pointer)
	TryGetIndex(obj interface{}, key interface{}) (interface{}, bool)
	GetIndex(obj interface{}, key interface{}) interface{}
	UnsafeGetIndex(obj unsafe.Pointer, key unsafe.Pointer) unsafe.Pointer
	Iterate(obj interface{}) MapIterator
	UnsafeIterate(obj unsafe.Pointer) MapIterator
}

type MapIterator interface {
	HasNext() bool
	Next() (key interface{}, elem interface{})
	UnsafeNext() (key unsafe.Pointer, elem unsafe.Pointer)
}

type PtrType interface {
	Type
	Elem() Type
}

type InterfaceType interface {
	NumMethod() int
}

type Config struct {
	UseSafeImplementation bool
}

type API interface {
	TypeOf(obj interface{}) Type
	Type2(type1 reflect.Type) Type
}

var ConfigUnsafe = Config{UseSafeImplementation: false}.Froze()
var ConfigSafe = Config{UseSafeImplementation: true}.Froze()

type frozenConfig struct {
	useSafeImplementation bool
	cache                 *concurrent.Map
}

func (cfg Config) Froze() *frozenConfig {
	return &frozenConfig{
		useSafeImplementation: cfg.UseSafeImplementation,
		cache: concurrent.NewMap(),
	}
}

func (cfg *frozenConfig) TypeOf(obj interface{}) Type {
	cacheKey := uintptr(unpackEFace(obj).rtype)
	typeObj, found := cfg.cache.Load(cacheKey)
	if found {
		return typeObj.(Type)
	}
	return cfg.Type2(reflect.TypeOf(obj))
}

func (cfg *frozenConfig) Type2(type1 reflect.Type) Type {
	cacheKey := uintptr(unpackEFace(type1).data)
	typeObj, found := cfg.cache.Load(cacheKey)
	if found {
		return typeObj.(Type)
	}
	type2 := cfg.wrapType(type1)
	cfg.cache.Store(cacheKey, type2)
	return type2
}

func (cfg *frozenConfig) wrapType(type1 reflect.Type) Type {
	safeType := safeType{Type: type1, cfg: cfg}
	switch type1.Kind() {
	case reflect.Struct:
		if cfg.useSafeImplementation {
			return &safeStructType{safeType}
		}
		return newUnsafeStructType(cfg, type1)
	case reflect.Array:
		if cfg.useSafeImplementation {
			return &safeSliceType{safeType}
		}
		return newUnsafeArrayType(cfg, type1)
	case reflect.Slice:
		if cfg.useSafeImplementation {
			return &safeSliceType{safeType}
		}
		return newUnsafeSliceType(cfg, type1)
	case reflect.Map:
		if cfg.useSafeImplementation {
			return &safeMapType{safeType}
		}
		return newUnsafeMapType(cfg, type1)
	case reflect.Ptr, reflect.Chan, reflect.Func:
		if cfg.useSafeImplementation {
			return &safeMapType{safeType}
		}
		return newUnsafePtrType(cfg, type1)
	case reflect.Interface:
		if cfg.useSafeImplementation {
			return &safeMapType{safeType}
		}
		if type1.NumMethod() == 0 {
			return newUnsafeEFaceType(cfg, type1)
		}
		return newUnsafeIFaceType(cfg, type1)
	default:
		if cfg.useSafeImplementation {
			return &safeType
		}
		return newUnsafeType(cfg, type1)
	}
}

func TypeOf(obj interface{}) Type {
	return ConfigUnsafe.TypeOf(obj)
}

func TypeOfPtr(obj interface{}) PtrType {
	return TypeOf(obj).(PtrType)
}

func Type2(type1 reflect.Type) Type {
	if type1 == nil {
		return nil
	}
	return ConfigUnsafe.Type2(type1)
}

func PtrTo(typ Type) Type {
	return Type2(reflect.PtrTo(typ.Type1()))
}

func PtrOf(obj interface{}) unsafe.Pointer {
	return unpackEFace(obj).data
}

func RTypeOf(obj interface{}) uintptr {
	return uintptr(unpackEFace(obj).rtype)
}

func IsNil(obj interface{}) bool {
	if obj == nil {
		return true
	}
	return unpackEFace(obj).data == nil
}

func IsNullable(kind reflect.Kind) bool {
	switch kind {
	case reflect.Ptr, reflect.Map, reflect.Chan, reflect.Func, reflect.Slice, reflect.Interface:
		return true
	}
	return false
}

func likePtrKind(kind reflect.Kind) bool {
	switch kind {
	case reflect.Ptr, reflect.Map, reflect.Chan, reflect.Func:
		return true
	}
	return false
}

func likePtrType(typ reflect.Type) bool {
	if likePtrKind(typ.Kind()) {
		return true
	}
	if typ.Kind() == reflect.Struct {
		if typ.NumField() != 1 {
			return false
		}
		return likePtrType(typ.Field(0).Type)
	}
	if typ.Kind() == reflect.Array {
		if typ.Len() != 1 {
			return false
		}
		return likePtrType(typ.Elem())
	}
	return false
}

// NoEscape hides a pointer from escape analysis.  noescape is
// the identity function but escape analysis doesn't think the
// output depends on the input.  noescape is inlined and currently
// compiles down to zero instructions.
// USE CAREFULLY!
//go:nosplit
func NoEscape(p unsafe.Pointer) unsafe.Pointer {
	x := uintptr(p)
	return unsafe.Pointer(x ^ 0)
}

func UnsafeCastString(str string) []byte {
	stringHeader := (*reflect.StringHeader)(unsafe.Pointer(&str))
	sliceHeader := &reflect.SliceHeader{
		Data: stringHeader.Data,
		Cap: stringHeader.Len,
		Len: stringHeader.Len,
	}
	return *(*[]byte)(unsafe.Pointer(sliceHeader))
}
<commit_msg>fix TypeOf(nil) panic<commit_after>package reflect2

import (
	"github.com/modern-go/concurrent"
	"reflect"
	"unsafe"
)

type Type interface {
	Kind() reflect.Kind
	// New return pointer to data of this type
	New() interface{}
	// UnsafeNew return the allocated space pointed by unsafe.Pointer
	UnsafeNew() unsafe.Pointer
	// PackEFace cast a unsafe pointer to object represented pointer
	PackEFace(ptr unsafe.Pointer) interface{}
	// Indirect dereference object represented pointer to this type
	Indirect(obj interface{}) interface{}
	// UnsafeIndirect dereference pointer to this type
	UnsafeIndirect(ptr unsafe.Pointer) interface{}
	// Type1 returns reflect.Type
	Type1() reflect.Type
	Implements(thatType Type) bool
	String() string
	RType() uintptr
	// interface{} of this type has pointer like behavior
	LikePtr() bool
	IsNullable() bool
	IsNil(obj interface{}) bool
	UnsafeIsNil(ptr unsafe.Pointer) bool
	Set(obj interface{}, val interface{})
	UnsafeSet(ptr unsafe.Pointer, val unsafe.Pointer)
	AssignableTo(anotherType Type) bool
}

type ListType interface {
	Type
	Elem() Type
	SetIndex(obj interface{}, index int, elem interface{})
	UnsafeSetIndex(obj unsafe.Pointer, index int, elem unsafe.Pointer)
	GetIndex(obj interface{}, index int) interface{}
	UnsafeGetIndex(obj unsafe.Pointer, index int) unsafe.Pointer
}

type ArrayType interface {
	ListType
	Len() int
}

type SliceType interface {
	ListType
	MakeSlice(length int, cap int) interface{}
	UnsafeMakeSlice(length int, cap int) unsafe.Pointer
	Grow(obj interface{}, newLength int)
	UnsafeGrow(ptr unsafe.Pointer, newLength int)
	Append(obj interface{}, elem interface{})
	UnsafeAppend(obj unsafe.Pointer, elem unsafe.Pointer)
	LengthOf(obj interface{}) int
	UnsafeLengthOf(ptr unsafe.Pointer) int
	SetNil(obj interface{})
	UnsafeSetNil(ptr unsafe.Pointer)
	Cap(obj interface{}) int
	UnsafeCap(ptr unsafe.Pointer) int
}

type StructType interface {
	Type
	NumField() int
	Field(i int) StructField
	FieldByName(name string) StructField
	FieldByIndex(index []int) StructField
	FieldByNameFunc(match func(string) bool) StructField
}

type StructField interface {
	Offset() uintptr
	Name() string
	PkgPath() string
	Type() Type
	Tag() reflect.StructTag
	Index() []int
	Anonymous() bool
	Set(obj interface{}, value interface{})
	UnsafeSet(obj unsafe.Pointer, value unsafe.Pointer)
	Get(obj interface{}) interface{}
	UnsafeGet(obj unsafe.Pointer) unsafe.Pointer
}

type MapType interface {
	Type
	Key() Type
	Elem() Type
	MakeMap(cap int) interface{}
	UnsafeMakeMap(cap int) unsafe.Pointer
	SetIndex(obj interface{}, key interface{}, elem interface{})
	UnsafeSetIndex(obj unsafe.Pointer, key unsafe.Pointer, elem unsafe.Pointer)
	TryGetIndex(obj interface{}, key interface{}) (interface{}, bool)
	GetIndex(obj interface{}, key interface{}) interface{}
	UnsafeGetIndex(obj unsafe.Pointer, key unsafe.Pointer) unsafe.Pointer
	Iterate(obj interface{}) MapIterator
	UnsafeIterate(obj unsafe.Pointer) MapIterator
}

type MapIterator interface {
	HasNext() bool
	Next() (key interface{}, elem interface{})
	UnsafeNext() (key unsafe.Pointer, elem unsafe.Pointer)
}

type PtrType interface {
	Type
	Elem() Type
}

type InterfaceType interface {
	NumMethod() int
}

type Config struct {
	UseSafeImplementation bool
}

type API interface {
	TypeOf(obj interface{}) Type
	Type2(type1 reflect.Type) Type
}

var ConfigUnsafe = Config{UseSafeImplementation: false}.Froze()
var ConfigSafe = Config{UseSafeImplementation: true}.Froze()

type frozenConfig struct {
	useSafeImplementation bool
	cache                 *concurrent.Map
}

func (cfg Config) Froze() *frozenConfig {
	return &frozenConfig{
		useSafeImplementation: cfg.UseSafeImplementation,
		cache: concurrent.NewMap(),
	}
}

func (cfg *frozenConfig) TypeOf(obj interface{}) Type {
	cacheKey := uintptr(unpackEFace(obj).rtype)
	typeObj, found := cfg.cache.Load(cacheKey)
	if found {
		return typeObj.(Type)
	}
	return cfg.Type2(reflect.TypeOf(obj))
}

func (cfg *frozenConfig) Type2(type1 reflect.Type) Type {
	if type1 == nil {
		return nil
	}
	cacheKey := uintptr(unpackEFace(type1).data)
	typeObj, found := cfg.cache.Load(cacheKey)
	if found {
		return typeObj.(Type)
	}
	type2 := cfg.wrapType(type1)
	cfg.cache.Store(cacheKey, type2)
	return type2
}

func (cfg *frozenConfig) wrapType(type1 reflect.Type) Type {
	safeType := safeType{Type: type1, cfg: cfg}
	switch type1.Kind() {
	case reflect.Struct:
		if cfg.useSafeImplementation {
			return &safeStructType{safeType}
		}
		return newUnsafeStructType(cfg, type1)
	case reflect.Array:
		if cfg.useSafeImplementation {
			return &safeSliceType{safeType}
		}
		return newUnsafeArrayType(cfg, type1)
	case reflect.Slice:
		if cfg.useSafeImplementation {
			return &safeSliceType{safeType}
		}
		return newUnsafeSliceType(cfg, type1)
	case reflect.Map:
		if cfg.useSafeImplementation {
			return &safeMapType{safeType}
		}
		return newUnsafeMapType(cfg, type1)
	case reflect.Ptr, reflect.Chan, reflect.Func:
		if cfg.useSafeImplementation {
			return &safeMapType{safeType}
		}
		return newUnsafePtrType(cfg, type1)
	case reflect.Interface:
		if cfg.useSafeImplementation {
			return &safeMapType{safeType}
		}
		if type1.NumMethod() == 0 {
			return newUnsafeEFaceType(cfg, type1)
		}
		return newUnsafeIFaceType(cfg, type1)
	default:
		if cfg.useSafeImplementation {
			return &safeType
		}
		return newUnsafeType(cfg, type1)
	}
}

func TypeOf(obj interface{}) Type {
	return ConfigUnsafe.TypeOf(obj)
}

func TypeOfPtr(obj interface{}) PtrType {
	return TypeOf(obj).(PtrType)
}

func Type2(type1 reflect.Type) Type {
	if type1 == nil {
		return nil
	}
	return ConfigUnsafe.Type2(type1)
}

func PtrTo(typ Type) Type {
	return Type2(reflect.PtrTo(typ.Type1()))
}

func PtrOf(obj interface{}) unsafe.Pointer {
	return unpackEFace(obj).data
}

func RTypeOf(obj interface{}) uintptr {
	return uintptr(unpackEFace(obj).rtype)
}

func IsNil(obj interface{}) bool {
	if obj == nil {
		return true
	}
	return unpackEFace(obj).data == nil
}

func IsNullable(kind reflect.Kind) bool {
	switch kind {
	case reflect.Ptr, reflect.Map, reflect.Chan, reflect.Func, reflect.Slice, reflect.Interface:
		return true
	}
	return false
}

func likePtrKind(kind reflect.Kind) bool {
	switch kind {
	case reflect.Ptr, reflect.Map, reflect.Chan, reflect.Func:
		return true
	}
	return false
}

func likePtrType(typ reflect.Type) bool {
	if likePtrKind(typ.Kind()) {
		return true
	}
	if typ.Kind() == reflect.Struct {
		if typ.NumField() != 1 {
			return false
		}
		return likePtrType(typ.Field(0).Type)
	}
	if typ.Kind() == reflect.Array {
		if typ.Len() != 1 {
			return false
		}
		return likePtrType(typ.Elem())
	}
	return false
}

// NoEscape hides a pointer from escape analysis.  noescape is
// the identity function but escape analysis doesn't think the
// output depends on the input.  noescape is inlined and currently
// compiles down to zero instructions.
// USE CAREFULLY!
//go:nosplit
func NoEscape(p unsafe.Pointer) unsafe.Pointer {
	x := uintptr(p)
	return unsafe.Pointer(x ^ 0)
}

func UnsafeCastString(str string) []byte {
	stringHeader := (*reflect.StringHeader)(unsafe.Pointer(&str))
	sliceHeader := &reflect.SliceHeader{
		Data: stringHeader.Data,
		Cap: stringHeader.Len,
		Len: stringHeader.Len,
	}
	return *(*[]byte)(unsafe.Pointer(sliceHeader))
}
<|endoftext|>