<commit_before>package whisper

import (
	"encoding/binary"
	"io"
	"os"
	"time"
)

// General metadata about a whisper database
type Metadata struct {
	AggregationMethod uint32  // Aggregation method used. See the AGGREGATION_* constants
	MaxRetention      uint32  // The maximum retention period
	XFilesFactor      float32 // The minimum percentage of known values required to aggregate
	ArchiveCount      uint32  // The number of archives in the database
}

// Metadata about an archive within the database
type ArchiveInfo struct {
	Offset          uint32 // The offset of the archive within the database
	SecondsPerPoint uint32 // The number of seconds of elapsed time represented by a data point
	Points          uint32 // The number of data points
}

func (a ArchiveInfo) Retention() uint32 {
	return a.SecondsPerPoint * a.Points
}

func (a ArchiveInfo) Size() uint32 {
	return a.Points * pointSize
}

func (a ArchiveInfo) End() uint32 {
	return a.Offset + a.Size()
}

// The whisper database header, contains metadata
type Header struct {
	Metadata Metadata
	Archives []ArchiveInfo
}

type Archive []Point

type Point struct {
	Timestamp uint32 // Timestamp in seconds past the epoch
	Value     float64 // Data point value
}

type Whisper struct {
	Header Header
	file   *os.File
}

var pointSize, metadataSize, archiveSize uint32

// Aggregation type using averaging
const AGGREGATION_AVERAGE = 1

// Aggregation type using sum
const AGGREGATION_SUM = 2

// Aggregation type using the last value
const AGGREGATION_LAST = 3

// Aggregation type using the maximum value
const AGGREGATION_MAX = 4

// Aggregation type using the minimum value
const AGGREGATION_MIN = 5

func init() {
	pointSize = uint32(binary.Size(Point{}))
	metadataSize = uint32(binary.Size(Metadata{}))
	archiveSize = uint32(binary.Size(Archive{}))
}

// Read the header of a whisper database
func readHeader(buf io.ReadSeeker) (header Header, err error) {
	currentPos, err := buf.Seek(0, 1)
	if err != nil {
		return
	}
	defer func() {
		// Try to return to the original position when we exit
		_, e := buf.Seek(currentPos, 0)
		if e != nil {
			err = e
		}
		return
	}()

	// Start at the beginning of the file
	_, err = buf.Seek(0, 0)
	if err != nil {
		return
	}

	// Read metadata
	var metadata Metadata
	err = binary.Read(buf, binary.BigEndian, metadata)
	if err != nil {
		return
	}
	header.Metadata = metadata

	// Read archive info
	archives := make([]ArchiveInfo, metadata.ArchiveCount)
	for i := uint32(0); i < metadata.ArchiveCount; i++ {
		err = binary.Read(buf, binary.BigEndian, archives[i])
		if err != nil {
			return
		}
	}
	header.Archives = archives

	return
}

// Create a new whisper database at a given file path
func Create(path string, archives []ArchiveInfo, xFilesFactor float32, aggregationMethod uint32, sparse bool) (err error) {
	file, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)

	oldest := uint32(0)
	for _, archive := range archives {
		age := archive.SecondsPerPoint * archive.Points
		if age > oldest {
			oldest = age
		}
	}

	metadata := Metadata{
		AggregationMethod: aggregationMethod,
		XFilesFactor:      xFilesFactor,
		ArchiveCount:      uint32(len(archives)),
		MaxRetention:      oldest,
	}
	err = binary.Write(file, binary.BigEndian, metadata)
	if err != nil {
		return
	}

	headerSize := metadataSize + (archiveSize * uint32(len(archives)))
	archiveOffsetPointer := headerSize

	for _, archive := range archives {
		archive.Offset = archiveOffsetPointer
		err = binary.Write(file, binary.BigEndian, archive)
		if err != nil {
			return
		}
		archiveOffsetPointer += archive.Points * pointSize
	}

	if sparse {
		file.Seek(int64(archiveOffsetPointer-headerSize-1), 0)
		file.Write([]byte{0})
	} else {
		remaining := archiveOffsetPointer - headerSize
		chunkSize := uint32(16384)
		buf := make([]byte, chunkSize)
		for remaining > chunkSize {
			file.Write(buf)
			remaining -= chunkSize
		}
		file.Write(buf[:remaining])
	}

	return
}

// Open a whisper database
func Open(path string) (whisper Whisper, err error) {
	file, err := os.OpenFile(path, os.O_RDWR, 0666)
	if err != nil {
		return
	}

	header, err := readHeader(file)
	if err != nil {
		return
	}
	whisper = Whisper{Header: header, file: file}
	return
}

// Write a single datapoint to the whisper database
func (w Whisper) Update(point Point) (err error) {
	now := uint32(time.Now().Unix())
	diff := now - point.Timestamp
	if !((diff < w.Header.Metadata.MaxRetention) && diff >= 0) {
		// TODO: Return an error
		return
	}

	// Find the higher-precision archive that covers the timestamp
	var lowerArchives []ArchiveInfo
	var currentArchive ArchiveInfo
	for i, currentArchive := range w.Header.Archives {
		if currentArchive.Retention() < diff {
			continue
		}
		lowerArchives = w.Header.Archives[i+1:]
	}

	// Normalize the point's timestamp to the current archive's precision
	point.Timestamp = point.Timestamp - (point.Timestamp % currentArchive.SecondsPerPoint)

	// Write the point
	offset, err := w.pointOffset(currentArchive, point.Timestamp)
	if err != nil {
		return
	}
	err = w.writePoint(offset, point)
	if err != nil {
		return
	}

	// Propagate data down to all the lower resolution archives
	higherArchive := currentArchive
	for _, lowerArchive := range lowerArchives {
		result, e := w.propagate(point.Timestamp, higherArchive, lowerArchive)
		if !result {
			break
		}
		if e != nil {
			err = e
			return
		}
		higherArchive = lowerArchive
	}

	return
}

func (w Whisper) propagate(timestamp uint32, higher ArchiveInfo, lower ArchiveInfo) (result bool, err error) {
	// The start of the lower resolution archive interval.
	// Essentially a downsampling of the higher resolution timestamp.
	lowerIntervalStart := timestamp - (timestamp % lower.SecondsPerPoint)

	// The offset of the first point in the higher resolution data to be propagated down
	higherFirstOffset, err := w.pointOffset(higher, lowerIntervalStart)
	if err != nil {
		return
	}

	// how many higher resolution points that go in to a lower resolution point
	numHigherPoints := lower.SecondsPerPoint / higher.SecondsPerPoint

	// The total size of the higher resolution points
	higherPointsSize := numHigherPoints * pointSize

	// The realtive offset of the first high res point
	relativeFirstOffset := higherFirstOffset - higher.Offset
	// The relative offset of the last high res point
	relativeLastOffset := (relativeFirstOffset + higherPointsSize) % higher.Size()

	// The actual offset of the last high res point
	higherLastOffset := relativeLastOffset + higher.Offset

	points, err := w.readPointsBetweenOffsets(higher, higherFirstOffset, higherLastOffset)
	if err != nil {
		return
	}
	neighborPoints := make([]Point, 0, len(points))

	currentInterval := lowerIntervalStart
	for i := 0; i < len(points); i += 2 {
		if points[i].Timestamp == currentInterval {
			neighborPoints = append(neighborPoints, points[i])
		}
		currentInterval += higher.SecondsPerPoint
	}

	knownPercent := float32(len(neighborPoints))/float32(len(points)) < w.Header.Metadata.XFilesFactor
	if len(neighborPoints) == 0 || knownPercent {
		// There's nothing to propagate
		return false, nil
	}

	aggregatePoint, err := aggregate(w.Header.Metadata.AggregationMethod, neighborPoints)
	if err != nil {
		return
	}
	aggregatePoint.Timestamp = lowerIntervalStart
	aggregateOffset, err := w.pointOffset(lower, aggregatePoint.Timestamp)
	if err != nil {
		return
	}

	err = w.writePoint(aggregateOffset, aggregatePoint)
	if err != nil {
		return
	}

	return true, nil

}

// Read a single point from an offset in the database
func (w Whisper) readPoint(offset uint32) (point Point, err error) {
	_, err = w.file.Seek(int64(offset), 0)
	if err != nil {
		return
	}
	err = binary.Read(w.file, binary.BigEndian, point)
	return
}

// Read a slice of points from an offset in the database
func (w Whisper) readPoints(offset uint32, points []Point) (err error) {
	w.file.Seek(int64(offset), 0)
	err = binary.Read(w.file, binary.BigEndian, points)
	return
}

func (w Whisper) readPointsBetweenOffsets(archive ArchiveInfo, startOffset, endOffset uint32) (points []Point, err error) {
	archiveStart := archive.Offset
	archiveEnd := archive.End()
	if startOffset < endOffset {
		// The selection is in the middle of the archive. eg: --####---
		points = make([]Point, (endOffset-startOffset)/pointSize)
		err = w.readPoints(startOffset, points)
		if err != nil {
			return
		}
	} else {
		// The selection wraps over the end of the archive. eg: ##----###
		numEndPoints := (archiveEnd - startOffset) / pointSize
		numBeginPoints := (endOffset - archiveStart) / pointSize
		points = make([]Point, numBeginPoints+numEndPoints)

		err = w.readPoints(startOffset, points[:numEndPoints])
		if err != nil {
			return
		}
		err = w.readPoints(archiveStart, points[numEndPoints:])
		if err != nil {
			return
		}
	}
	return
}

// Write a point to an offset in the database
func (w Whisper) writePoint(offset uint32, point Point) (err error) {
	w.file.Seek(int64(offset), 0)
	err = binary.Write(w.file, binary.BigEndian, point)
	return
}

// Get the offset of a timestamp within an archive
func (w Whisper) pointOffset(archive ArchiveInfo, timestamp uint32) (offset uint32, err error) {
	basePoint, err := w.readPoint(0)
	if err != nil {
		return
	}
	if basePoint.Timestamp == 0 {
		// The archive has never been written, this will be the new base point
		offset = archive.Offset
	} else {
		timeDistance := timestamp - basePoint.Timestamp
		pointDistance := timeDistance / archive.SecondsPerPoint
		byteDistance := pointDistance * pointSize
		offset = archive.Offset + (byteDistance % archive.Size())
	}
	return
}

func aggregate(aggregationMethod uint32, points []Point) (point Point, err error) {
	switch aggregationMethod {
	case AGGREGATION_AVERAGE:
		for _, p := range points {
			point.Value += p.Value
		}
		point.Value /= float64(len(points))
	case AGGREGATION_SUM:
		for _, p := range points {
			point.Value += p.Value
		}
	case AGGREGATION_LAST:
		point.Value = points[len(points)-1].Value
	case AGGREGATION_MAX:
		point.Value = points[0].Value
		for _, p := range points {
			if p.Value > point.Value {
				point.Value = p.Value
			}
		}
	case AGGREGATION_MIN:
		point.Value = points[0].Value
		for _, p := range points {
			if p.Value < point.Value {
				point.Value = p.Value
			}
		}
	default:
		//TODO: Set err
	}
	return
}
<commit_msg>Implemented archive list validation<commit_after>package whisper

import (
	"encoding/binary"
	"errors"
	"io"
	"os"
	"sort"
	"time"
)

// General metadata about a whisper database
type Metadata struct {
	AggregationMethod uint32  // Aggregation method used. See the AGGREGATION_* constants
	MaxRetention      uint32  // The maximum retention period
	XFilesFactor      float32 // The minimum percentage of known values required to aggregate
	ArchiveCount      uint32  // The number of archives in the database
}

// Metadata about an archive within the database
type ArchiveInfo struct {
	Offset          uint32 // The offset of the archive within the database
	SecondsPerPoint uint32 // The number of seconds of elapsed time represented by a data point
	Points          uint32 // The number of data points
}

func (a ArchiveInfo) Retention() uint32 {
	return a.SecondsPerPoint * a.Points
}

func (a ArchiveInfo) Size() uint32 {
	return a.Points * pointSize
}

func (a ArchiveInfo) End() uint32 {
	return a.Offset + a.Size()
}

type ArchiveInfos []*ArchiveInfo

func (a ArchiveInfos) Len() int           { return len(a) }
func (a ArchiveInfos) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ArchiveInfos) Less(i, j int) bool { return a[i].SecondsPerPoint < a[j].SecondsPerPoint }

// The whisper database header, contains metadata
type Header struct {
	Metadata Metadata
	Archives ArchiveInfos
}

type Archive []Point

type Point struct {
	Timestamp uint32  // Timestamp in seconds past the epoch
	Value     float64 // Data point value
}

type Whisper struct {
	Header Header
	file   *os.File
}

var pointSize, metadataSize, archiveSize uint32

// Aggregation type using averaging
const AGGREGATION_AVERAGE = 1

// Aggregation type using sum
const AGGREGATION_SUM = 2

// Aggregation type using the last value
const AGGREGATION_LAST = 3

// Aggregation type using the maximum value
const AGGREGATION_MAX = 4

// Aggregation type using the minimum value
const AGGREGATION_MIN = 5

func init() {
	pointSize = uint32(binary.Size(Point{}))
	metadataSize = uint32(binary.Size(Metadata{}))
	archiveSize = uint32(binary.Size(Archive{}))
}

// Read the header of a whisper database
func readHeader(buf io.ReadSeeker) (header Header, err error) {
	currentPos, err := buf.Seek(0, 1)
	if err != nil {
		return
	}
	defer func() {
		// Try to return to the original position when we exit
		_, e := buf.Seek(currentPos, 0)
		if e != nil {
			err = e
		}
		return
	}()

	// Start at the beginning of the file
	_, err = buf.Seek(0, 0)
	if err != nil {
		return
	}

	// Read metadata
	var metadata Metadata
	err = binary.Read(buf, binary.BigEndian, metadata)
	if err != nil {
		return
	}
	header.Metadata = metadata

	// Read archive info
	archives := make(ArchiveInfos, metadata.ArchiveCount)
	for i := uint32(0); i < metadata.ArchiveCount; i++ {
		err = binary.Read(buf, binary.BigEndian, archives[i])
		if err != nil {
			return
		}
	}
	header.Archives = archives

	return
}

/*

Validates a list of ArchiveInfos

The list must:

1. Have at least one ArchiveInfo

2. No archive may be a duplicate of another.

3. Higher precision archives' precision must evenly divide all lower precision archives' precision.

4. Lower precision archives must cover larger time intervals than higher precision archives.

5. Each archive must have at least enough points to consolidate to the next archive

*/
func ValidateArchiveList(archives ArchiveInfos) error {
	//TODO: Better error messages for this function

	sort.Sort(archives)

	// 1.
	if len(archives) == 0 {
		return errors.New("archive list cannot have 0 length")
	}

	for i, archive := range archives {
		if i == (len(archives) - 1) {
			break
		}

		// 2.
		nextArchive := archives[i+1]
		if !(archive.SecondsPerPoint < nextArchive.SecondsPerPoint) {
			return errors.New("No archive may be a duplicate of another")
		}

		// 3.
		if nextArchive.SecondsPerPoint%archive.SecondsPerPoint != 0 {
			return errors.New("Higher precision archives must evenly divide in to lower precision")
		}

		// 4.
		nextRetention := nextArchive.Retention()
		retention := archive.Retention()
		if !(nextRetention > retention) {
			return errors.New("Lower precision archives must cover a larger time interval than higher precision")
		}

		// 5.
		if !(archive.Points >= (nextArchive.SecondsPerPoint / archive.SecondsPerPoint)) {
			return errors.New("Each archive must be able to consolidate the next")
		}

	}
	return nil

}

// Create a new whisper database at a given file path
func Create(path string, archives ArchiveInfos, xFilesFactor float32, aggregationMethod uint32, sparse bool) (err error) {
	file, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)

	oldest := uint32(0)
	for _, archive := range archives {
		age := archive.SecondsPerPoint * archive.Points
		if age > oldest {
			oldest = age
		}
	}

	metadata := Metadata{
		AggregationMethod: aggregationMethod,
		XFilesFactor:      xFilesFactor,
		ArchiveCount:      uint32(len(archives)),
		MaxRetention:      oldest,
	}
	err = binary.Write(file, binary.BigEndian, metadata)
	if err != nil {
		return
	}

	headerSize := metadataSize + (archiveSize * uint32(len(archives)))
	archiveOffsetPointer := headerSize

	for _, archive := range archives {
		archive.Offset = archiveOffsetPointer
		err = binary.Write(file, binary.BigEndian, archive)
		if err != nil {
			return
		}
		archiveOffsetPointer += archive.Points * pointSize
	}

	if sparse {
		file.Seek(int64(archiveOffsetPointer-headerSize-1), 0)
		file.Write([]byte{0})
	} else {
		remaining := archiveOffsetPointer - headerSize
		chunkSize := uint32(16384)
		buf := make([]byte, chunkSize)
		for remaining > chunkSize {
			file.Write(buf)
			remaining -= chunkSize
		}
		file.Write(buf[:remaining])
	}

	return
}

// Open a whisper database
func Open(path string) (whisper Whisper, err error) {
	file, err := os.OpenFile(path, os.O_RDWR, 0666)
	if err != nil {
		return
	}

	header, err := readHeader(file)
	if err != nil {
		return
	}
	whisper = Whisper{Header: header, file: file}
	return
}

// Write a single datapoint to the whisper database
func (w Whisper) Update(point Point) (err error) {
	now := uint32(time.Now().Unix())
	diff := now - point.Timestamp
	if !((diff < w.Header.Metadata.MaxRetention) && diff >= 0) {
		// TODO: Return an error
		return
	}

	// Find the higher-precision archive that covers the timestamp
	var lowerArchives ArchiveInfos
	var currentArchive *ArchiveInfo
	for i, currentArchive := range w.Header.Archives {
		if currentArchive.Retention() < diff {
			continue
		}
		lowerArchives = w.Header.Archives[i+1:]
	}

	// Normalize the point's timestamp to the current archive's precision
	point.Timestamp = point.Timestamp - (point.Timestamp % currentArchive.SecondsPerPoint)

	// Write the point
	offset, err := w.pointOffset(currentArchive, point.Timestamp)
	if err != nil {
		return
	}
	err = w.writePoint(offset, point)
	if err != nil {
		return
	}

	// Propagate data down to all the lower resolution archives
	higherArchive := currentArchive
	for _, lowerArchive := range lowerArchives {
		result, e := w.propagate(point.Timestamp, higherArchive, lowerArchive)
		if !result {
			break
		}
		if e != nil {
			err = e
			return
		}
		higherArchive = lowerArchive
	}

	return
}

func (w Whisper) propagate(timestamp uint32, higher *ArchiveInfo, lower *ArchiveInfo) (result bool, err error) {
	// The start of the lower resolution archive interval.
	// Essentially a downsampling of the higher resolution timestamp.
	lowerIntervalStart := timestamp - (timestamp % lower.SecondsPerPoint)

	// The offset of the first point in the higher resolution data to be propagated down
	higherFirstOffset, err := w.pointOffset(higher, lowerIntervalStart)
	if err != nil {
		return
	}

	// how many higher resolution points that go in to a lower resolution point
	numHigherPoints := lower.SecondsPerPoint / higher.SecondsPerPoint

	// The total size of the higher resolution points
	higherPointsSize := numHigherPoints * pointSize

	// The realtive offset of the first high res point
	relativeFirstOffset := higherFirstOffset - higher.Offset
	// The relative offset of the last high res point
	relativeLastOffset := (relativeFirstOffset + higherPointsSize) % higher.Size()

	// The actual offset of the last high res point
	higherLastOffset := relativeLastOffset + higher.Offset

	points, err := w.readPointsBetweenOffsets(higher, higherFirstOffset, higherLastOffset)
	if err != nil {
		return
	}
	neighborPoints := make([]Point, 0, len(points))

	currentInterval := lowerIntervalStart
	for i := 0; i < len(points); i += 2 {
		if points[i].Timestamp == currentInterval {
			neighborPoints = append(neighborPoints, points[i])
		}
		currentInterval += higher.SecondsPerPoint
	}

	knownPercent := float32(len(neighborPoints))/float32(len(points)) < w.Header.Metadata.XFilesFactor
	if len(neighborPoints) == 0 || knownPercent {
		// There's nothing to propagate
		return false, nil
	}

	aggregatePoint, err := aggregate(w.Header.Metadata.AggregationMethod, neighborPoints)
	if err != nil {
		return
	}
	aggregatePoint.Timestamp = lowerIntervalStart
	aggregateOffset, err := w.pointOffset(lower, aggregatePoint.Timestamp)
	if err != nil {
		return
	}

	err = w.writePoint(aggregateOffset, aggregatePoint)
	if err != nil {
		return
	}

	return true, nil

}

/*

Set the aggregation method for the database

The value of aggregationMethod must be one of the AGGREGATION_* constants

*/
func (w Whisper) setAggregationMethod(aggregationMethod uint32) (err error) {
	//TODO: Validate the value of aggregationMethod

	w.Header.Metadata.AggregationMethod = aggregationMethod
	_, err = w.file.Seek(0, 0)
	if err != nil {
		return
	}

	err = binary.Write(w.file, binary.BigEndian, w.Header.Metadata)
	return
}

// Read a single point from an offset in the database
func (w Whisper) readPoint(offset uint32) (point Point, err error) {
	_, err = w.file.Seek(int64(offset), 0)
	if err != nil {
		return
	}
	err = binary.Read(w.file, binary.BigEndian, point)
	return
}

// Read a slice of points from an offset in the database
func (w Whisper) readPoints(offset uint32, points []Point) (err error) {
	w.file.Seek(int64(offset), 0)
	err = binary.Read(w.file, binary.BigEndian, points)
	return
}

func (w Whisper) readPointsBetweenOffsets(archive *ArchiveInfo, startOffset, endOffset uint32) (points []Point, err error) {
	archiveStart := archive.Offset
	archiveEnd := archive.End()
	if startOffset < endOffset {
		// The selection is in the middle of the archive. eg: --####---
		points = make([]Point, (endOffset-startOffset)/pointSize)
		err = w.readPoints(startOffset, points)
		if err != nil {
			return
		}
	} else {
		// The selection wraps over the end of the archive. eg: ##----###
		numEndPoints := (archiveEnd - startOffset) / pointSize
		numBeginPoints := (endOffset - archiveStart) / pointSize
		points = make([]Point, numBeginPoints+numEndPoints)

		err = w.readPoints(startOffset, points[:numEndPoints])
		if err != nil {
			return
		}
		err = w.readPoints(archiveStart, points[numEndPoints:])
		if err != nil {
			return
		}
	}
	return
}

// Write a point to an offset in the database
func (w Whisper) writePoint(offset uint32, point Point) (err error) {
	w.file.Seek(int64(offset), 0)
	err = binary.Write(w.file, binary.BigEndian, point)
	return
}

// Get the offset of a timestamp within an archive
func (w Whisper) pointOffset(archive *ArchiveInfo, timestamp uint32) (offset uint32, err error) {
	basePoint, err := w.readPoint(0)
	if err != nil {
		return
	}
	if basePoint.Timestamp == 0 {
		// The archive has never been written, this will be the new base point
		offset = archive.Offset
	} else {
		timeDistance := timestamp - basePoint.Timestamp
		pointDistance := timeDistance / archive.SecondsPerPoint
		byteDistance := pointDistance * pointSize
		offset = archive.Offset + (byteDistance % archive.Size())
	}
	return
}

func aggregate(aggregationMethod uint32, points []Point) (point Point, err error) {
	switch aggregationMethod {
	case AGGREGATION_AVERAGE:
		for _, p := range points {
			point.Value += p.Value
		}
		point.Value /= float64(len(points))
	case AGGREGATION_SUM:
		for _, p := range points {
			point.Value += p.Value
		}
	case AGGREGATION_LAST:
		point.Value = points[len(points)-1].Value
	case AGGREGATION_MAX:
		point.Value = points[0].Value
		for _, p := range points {
			if p.Value > point.Value {
				point.Value = p.Value
			}
		}
	case AGGREGATION_MIN:
		point.Value = points[0].Value
		for _, p := range points {
			if p.Value < point.Value {
				point.Value = p.Value
			}
		}
	default:
		//TODO: Set err
	}
	return
}
<|endoftext|>


<commit_before>// Package rtl provides bindings to the rtl-sdr library.
package rtl

// #cgo LDFLAGS: -lrtlsdr
// #include <stdlib.h>
// #include <rtl-sdr.h>
import "C"

import (
	"errors"
	"runtime"
	"unsafe"
)

var (
	ErrFailed           = errors.New("rtl: operation failed")
	ErrNoDevices        = errors.New("rtl: no devices")
	ErrNoMatchingDevice = errors.New("rtl: no matching device")
)

type TunerType int

const (
	TunerTypeUnknown TunerType = C.RTLSDR_TUNER_UNKNOWN
	TunerTypeE4000   TunerType = C.RTLSDR_TUNER_E4000
	TunerTypeFC0012  TunerType = C.RTLSDR_TUNER_FC0012
	TunerTypeFC0013  TunerType = C.RTLSDR_TUNER_FC0013
	TunerTypeFC2580  TunerType = C.RTLSDR_TUNER_FC2580
	TunerTypeR820T   TunerType = C.RTLSDR_TUNER_R820T
)

var (
	tunerTypeNames = map[TunerType]string{
		TunerTypeUnknown: "Unknown",
		TunerTypeE4000:   "E4000",
		TunerTypeFC0012:  "FC0012",
		TunerTypeFC0013:  "FC0013",
		TunerTypeFC2580:  "FC2580",
		TunerTypeR820T:   "R820T",
	}
)

func (tt TunerType) String() string {
	if name := tunerTypeNames[tt]; name == "" {
		return "Other"
	} else {
		return name
	}
}

type Device struct {
	cDev *C.rtlsdr_dev_t
}

func GetDeviceCount() int {
	return int(C.rtlsdr_get_device_count())
}

func GetDeviceName(index int) string {
	cName := C.rtlsdr_get_device_name(C.uint32_t(index))
	return C.GoString(cName)
}

// Get USB device strings.
// Return manufacturer, product name, and serial number
func GetDeviceUSBStrings(index int) (string, string, string, error) {
	var manufact [256]C.char
	var product [256]C.char
	var serial [256]C.char
	if C.rtlsdr_get_device_usb_strings(C.uint32_t(index), (*C.char)(&manufact[0]), (*C.char)(&product[0]), (*C.char)(&serial[0])) != 0 {
		return "", "", "", ErrFailed
	}
	return C.GoString((*C.char)(&manufact[0])), C.GoString((*C.char)(&product[0])), C.GoString((*C.char)(&serial[0])), nil
}

// Get device index by USB serial string descriptor.
func GetIndexBySerial(serial string) (int, error) {
	cSerial := C.CString(serial)
	defer C.free(unsafe.Pointer(cSerial))
	res := C.rtlsdr_get_index_by_serial(cSerial)
	if res < 0 {
		switch res {
		case -1:
			// Name is NULL. Shouldn't ever happen.
			return -1, ErrFailed
		case -2:
			return -1, ErrNoDevices
		case -3:
			return -1, ErrNoMatchingDevice
		default:
			return -1, ErrFailed
		}
	}
	return int(res), nil
}

func Open(index int) (*Device, error) {
	dev := &Device{}
	if C.rtlsdr_open(&dev.cDev, C.uint32_t(index)) < 0 {
		return nil, ErrFailed
	}
	runtime.SetFinalizer(dev, func(dev *Device) { dev.Close() })
	return dev, nil
}

func (dev *Device) Close() error {
	if dev.cDev != nil {
		if C.rtlsdr_close(dev.cDev) < 0 {
			return ErrFailed
		}
		dev.cDev = nil
	}
	return nil
}

// Get actual frequency the device is tuned to in Hz.
func (dev *Device) GetCenterFreq() (uint, error) {
	freq := C.rtlsdr_get_center_freq(dev.cDev)
	if freq == 0 {
		return 0, ErrFailed
	}
	return uint(freq), nil
}

func (dev *Device) SetCenterFreq(freq uint) error {
	if C.rtlsdr_set_center_freq(dev.cDev, C.uint32_t(freq)) != 0 {
		return ErrFailed
	}
	return nil
}

func (dev *Device) GetTunerType() TunerType {
	return TunerType(C.rtlsdr_get_tuner_type(dev.cDev))
}

// Get a list of gains supported by the tuner.
func (dev *Device) GetTunerGains() ([]int, error) {
	nGains := C.rtlsdr_get_tuner_gains(dev.cDev, nil)
	if nGains <= 0 {
		return nil, ErrFailed
	}
	cGains := make([]C.int, nGains)
	C.rtlsdr_get_tuner_gains(dev.cDev, &cGains[0])
	gains := make([]int, nGains)
	for i := 0; i < len(gains); i++ {
		gains[i] = int(cGains[i])
	}
	return gains, nil
}

// Set the gain for the device.
// Manual gain mode must be enabled for this to work.
func (dev *Device) SetTunerGain(gain int) error {
	if C.rtlsdr_set_tuner_gain(dev.cDev, C.int(gain)) < 0 {
		return ErrFailed
	}
	return nil
}

// Get actual gain the device is configured to in tength of a dB (115 means 11.5 dB)
func (dev *Device) GetTunerGain() (int, error) {
	if gain := C.rtlsdr_get_tuner_gain(dev.cDev); gain == 0 {
		return 0, ErrFailed
	} else {
		return int(gain), nil
	}
}

// Set the intermediate frequency gain for the device.
// - stage: intermediate frequency gain stage number (1 to 6 for E4000)
// - gain: tenths of a dB, -30 means -3.0 dB
func (dev *Device) SetTunerIfGain(stage, gain int) error {
	if C.rtlsdr_set_tuner_if_gain(dev.cDev, C.int(stage), C.int(gain)) < 0 {
		return ErrFailed
	}
	return nil
}

// Set the gain mode (automatic/manual) for the device.
// Manual gain mode must be enabled for the gain setter function to work.
func (dev *Device) SetTunerGainMode(manual bool) error {
	cManual := C.int(0)
	if manual {
		cManual = 1
	}
	if C.rtlsdr_set_tuner_gain_mode(dev.cDev, cManual) != 0 {
		return ErrFailed
	}
	return nil
}

// select the baseband filters according to the requested sample rate
func (dev *Device) SetSampleRate(rate uint) error {
	if C.rtlsdr_set_sample_rate(dev.cDev, C.uint32_t(rate)) != 0 {
		return ErrFailed
	}
	return nil
}
<commit_msg>Add more functions including synchronous read<commit_after>// Package rtl provides bindings to the rtl-sdr library.
package rtl

// #cgo LDFLAGS: -lrtlsdr
// #include <stdlib.h>
// #include <rtl-sdr.h>
import "C"

import (
	"errors"
	"runtime"
	"unsafe"
)

var (
	ErrFailed           = errors.New("rtl: operation failed")
	ErrNoDevices        = errors.New("rtl: no devices")
	ErrNoMatchingDevice = errors.New("rtl: no matching device")
)

type TunerType int

const (
	TunerTypeUnknown TunerType = C.RTLSDR_TUNER_UNKNOWN
	TunerTypeE4000   TunerType = C.RTLSDR_TUNER_E4000
	TunerTypeFC0012  TunerType = C.RTLSDR_TUNER_FC0012
	TunerTypeFC0013  TunerType = C.RTLSDR_TUNER_FC0013
	TunerTypeFC2580  TunerType = C.RTLSDR_TUNER_FC2580
	TunerTypeR820T   TunerType = C.RTLSDR_TUNER_R820T
)

var (
	tunerTypeNames = map[TunerType]string{
		TunerTypeUnknown: "Unknown",
		TunerTypeE4000:   "E4000",
		TunerTypeFC0012:  "FC0012",
		TunerTypeFC0013:  "FC0013",
		TunerTypeFC2580:  "FC2580",
		TunerTypeR820T:   "R820T",
	}
)

func (tt TunerType) String() string {
	if name := tunerTypeNames[tt]; name == "" {
		return "Other"
	} else {
		return name
	}
}

type Device struct {
	cDev *C.rtlsdr_dev_t
}

func GetDeviceCount() int {
	return int(C.rtlsdr_get_device_count())
}

func GetDeviceName(index int) string {
	cName := C.rtlsdr_get_device_name(C.uint32_t(index))
	return C.GoString(cName)
}

// Get USB device strings.
// Return manufacturer, product name, and serial number
func GetDeviceUSBStrings(index int) (string, string, string, error) {
	var manufact [256]C.char
	var product [256]C.char
	var serial [256]C.char
	if C.rtlsdr_get_device_usb_strings(C.uint32_t(index), (*C.char)(&manufact[0]), (*C.char)(&product[0]), (*C.char)(&serial[0])) != 0 {
		return "", "", "", ErrFailed
	}
	return C.GoString((*C.char)(&manufact[0])), C.GoString((*C.char)(&product[0])), C.GoString((*C.char)(&serial[0])), nil
}

// Get device index by USB serial string descriptor.
func GetIndexBySerial(serial string) (int, error) {
	cSerial := C.CString(serial)
	defer C.free(unsafe.Pointer(cSerial))
	res := C.rtlsdr_get_index_by_serial(cSerial)
	if res < 0 {
		switch res {
		case -1:
			// Name is NULL. Shouldn't ever happen.
			return -1, ErrFailed
		case -2:
			return -1, ErrNoDevices
		case -3:
			return -1, ErrNoMatchingDevice
		default:
			return -1, ErrFailed
		}
	}
	return int(res), nil
}

func Open(index int) (*Device, error) {
	dev := &Device{}
	if C.rtlsdr_open(&dev.cDev, C.uint32_t(index)) < 0 {
		return nil, ErrFailed
	}
	runtime.SetFinalizer(dev, func(dev *Device) { dev.Close() })
	return dev, nil
}

func (dev *Device) Close() error {
	if dev.cDev != nil {
		if C.rtlsdr_close(dev.cDev) < 0 {
			return ErrFailed
		}
		dev.cDev = nil
	}
	return nil
}

// Get actual frequency the device is tuned to in Hz.
func (dev *Device) GetCenterFreq() (uint, error) {
	freq := C.rtlsdr_get_center_freq(dev.cDev)
	if freq == 0 {
		return 0, ErrFailed
	}
	return uint(freq), nil
}

func (dev *Device) SetCenterFreq(freq uint) error {
	if C.rtlsdr_set_center_freq(dev.cDev, C.uint32_t(freq)) != 0 {
		return ErrFailed
	}
	return nil
}

func (dev *Device) GetTunerType() TunerType {
	return TunerType(C.rtlsdr_get_tuner_type(dev.cDev))
}

// Get a list of gains supported by the tuner.
func (dev *Device) GetTunerGains() ([]int, error) {
	nGains := C.rtlsdr_get_tuner_gains(dev.cDev, nil)
	if nGains <= 0 {
		return nil, ErrFailed
	}
	cGains := make([]C.int, nGains)
	C.rtlsdr_get_tuner_gains(dev.cDev, &cGains[0])
	gains := make([]int, nGains)
	for i := 0; i < len(gains); i++ {
		gains[i] = int(cGains[i])
	}
	return gains, nil
}

// Set the gain for the device.
// Manual gain mode must be enabled for this to work.
func (dev *Device) SetTunerGain(gain int) error {
	if C.rtlsdr_set_tuner_gain(dev.cDev, C.int(gain)) < 0 {
		return ErrFailed
	}
	return nil
}

// Get actual gain the device is configured to in tength of a dB (115 means 11.5 dB)
func (dev *Device) GetTunerGain() (int, error) {
	if gain := C.rtlsdr_get_tuner_gain(dev.cDev); gain == 0 {
		return 0, ErrFailed
	} else {
		return int(gain), nil
	}
}

// Set the intermediate frequency gain for the device.
// - stage: intermediate frequency gain stage number (1 to 6 for E4000)
// - gain: tenths of a dB, -30 means -3.0 dB
func (dev *Device) SetTunerIfGain(stage, gain int) error {
	if C.rtlsdr_set_tuner_if_gain(dev.cDev, C.int(stage), C.int(gain)) < 0 {
		return ErrFailed
	}
	return nil
}

// Set the gain mode (automatic/manual) for the device.
// Manual gain mode must be enabled for the gain setter function to work.
func (dev *Device) SetTunerGainMode(manual bool) error {
	cManual := C.int(0)
	if manual {
		cManual = 1
	}
	if C.rtlsdr_set_tuner_gain_mode(dev.cDev, cManual) != 0 {
		return ErrFailed
	}
	return nil
}

// Enable or disable the internal digital AGC of the RTL2832.
func (dev *Device) SetAGCMode(enabled bool) error {
	cEnabled := C.int(0)
	if enabled {
		cEnabled = 1
	}
	if C.rtlsdr_set_agc_mode(dev.cDev, cEnabled) != 0 {
		return ErrFailed
	}
	return nil
}

// Select the baseband filters according to the requested sample rate
func (dev *Device) SetSampleRate(rate uint) error {
	if C.rtlsdr_set_sample_rate(dev.cDev, C.uint32_t(rate)) != 0 {
		return ErrFailed
	}
	return nil
}

// Get actual sample rate the device is configured to
func (dev *Device) GetSampleRate() (int, error) {
	if sampleRate := C.rtlsdr_get_sample_rate(dev.cDev); sampleRate == 0 {
		return 0, ErrFailed
	} else {
		return int(sampleRate), nil
	}
}

func (dev *Device) ResetBuffer() error {
	if C.rtlsdr_reset_buffer(dev.cDev) < 0 {
		return ErrFailed
	}
	return nil
}

func (dev *Device) Read(buf []byte) (int, error) {
	var nRead C.int
	if C.rtlsdr_read_sync(dev.cDev, unsafe.Pointer(&buf[0]), C.int(len(buf)), &nRead) != 0 {
		return 0, ErrFailed
	}
	return int(nRead), nil
}
<|endoftext|>


<commit_before>package pi

import (
	"net/http"
	"fmt"
)

// The HandlerFunction type is an adapter to allow the use of ordinary functions as route handlers.
type HandlerFunction func(*RequestContext) error

// ServeFileHandler replies to the request with the contents of the named file or directory.
// For example:
// p := New()
// p.Router("/files",
// 		p.Route("/{file}").Get(ServeFileHandler("/tmp", true)),
// ).Get(ServeFileHandler("C:\\"))
func ServeFileHandler(path string, allowBrowsing bool) HandlerFunction {
	return func(c *RequestContext) error {
		if allowBrowsing {
			fullPath := c.R.URL.String()
			if len(fullPath) > len(c.RouteURL) {
				path += fullPath[len(c.RouteURL):]
			}
		}
		http.ServeFile(c.W, c.R, path)
		return nil
	}
}

// ErrorHandler returns the status code string representation and set the status code as specified.
// See http.StatusText.
func ErrorHandler(statusCode int) HandlerFunction {
	return func(c *RequestContext) error {
		c.SetStatusCode(statusCode)
		c.WriteString(http.StatusText(statusCode))
		return nil
	}
}
<commit_msg>Fixed example<commit_after>package pi

import (
	"net/http"
)

// The HandlerFunction type is an adapter to allow the use of ordinary functions as route handlers.
type HandlerFunction func(*RequestContext) error

// ServeFileHandler replies to the request with the contents of the named file or directory.
// For example:
// p := New()
// p.Router("/files").Get(ServeFileHandler("/tmp"))
func ServeFileHandler(path string, allowBrowsing bool) HandlerFunction {
	return func(c *RequestContext) error {
		if allowBrowsing {
			fullPath := c.R.URL.String()
			if len(fullPath) > len(c.RouteURL) {
				path += fullPath[len(c.RouteURL):]
			}
		}
		http.ServeFile(c.W, c.R, path)
		return nil
	}
}

// ErrorHandler returns the status code string representation and set the status code as specified.
// See http.StatusText.
func ErrorHandler(statusCode int) HandlerFunction {
	return func(c *RequestContext) error {
		c.SetStatusCode(statusCode)
		c.WriteString(http.StatusText(statusCode))
		return nil
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/boltdb/bolt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"time"
)

// TopVersion is the current level of patches that the database knows of.
// This should always be the total amount of migrations.
//
// If you add a new migration, increase this number to make sure that your
// migration is actually applied.
const TopVersion = 4

var (
	errNoMigrationsYet = errors.New("no migrations have been added yet")
	levelKey           = []byte("level")
)

// migrations is a list of all the migration functions.
// Their indexes are used to determine when they are to be applied.
var migrations = []func(db *bolt.DB) error{
	InitialMigration,
	MigrateOriginalColorPreferredColor,
	MigrateTournamentRunnerupStringPerson,
	MigrateMatchScoreOrder,
}

// Migrate is the main migration entrypoint
//
// When called, it will check the database to see what migrations have already
// been applied. If that is lower than the const TopVersion, all migrations up
// to that point will sequentially be applied.
func Migrate(db *bolt.DB) error {
	version, err := getVersion(db)

	if err == errNoMigrationsYet {
		// No migrations have been done yet, so lets do the first one by adding
		// the migration bucket.
		version = 0
	} else if err != nil {
		// Something actually went wrong. Oh noes.
		return err
	}

	// If version is lower than the latest known version, it's time to apply the
	// migrations!
	if version < TopVersion {
		if err := applyMigrations(db, version); err != nil {
			log.Print("Error: Migration application failed ;'(")
			return err
		}
	} else {
		log.Print("Database up to date.")
	}

	return nil
}

func applyMigrations(db *bolt.DB, version int) error {
	log.Printf(" --- Migrating %d -> %d:\n", version, TopVersion)
	if err := backup(db, version, "db-migration-backup/"); err != nil {
		return err
	}

	// Run the new migrations and the new migrations only
	for x, migration := range migrations[version:] {
		log.Printf("     Applying migration %d\n", x)
		if err := migration(db); err != nil {
			log.Print("     Migration failure: ", err)
			return err
		}
	}

	log.Printf(" --- Migrations applied successfully. <3\n")
	return nil
}

func backup(db *bolt.DB, version int, path string) error {
	_ = os.Mkdir(path, 0755)

	fn := fmt.Sprintf(
		"%d_from-v%d-to-v%d.db",
		time.Now().UnixNano(),
		version,
		TopVersion,
	)
	dst := filepath.Join(path, fn)

	data, _ := ioutil.ReadFile(db.Path())
	if err := ioutil.WriteFile(dst, data, 0644); err != nil {
		return err
	}

	log.Printf("     Backed up into %s\n", fn)
	return nil
}

func getVersion(db *bolt.DB) (int, error) {
	var version int
	err := db.View(func(tx *bolt.Tx) error {
		var err error
		b := tx.Bucket(MigrationKey)
		if b == nil {
			return errNoMigrationsYet
		}

		x := b.Get(levelKey)
		version, err = strconv.Atoi(string(x))
		if err != nil {
			return err
		}

		return nil
	})

	return version, err
}

func setVersion(tx *bolt.Tx, version int) error {
	b, err := tx.CreateBucketIfNotExists(MigrationKey)
	if err != nil {
		return err
	}

	out, _ := json.Marshal(version)
	if err := b.Put(levelKey, out); err != nil {
		return err
	}
	return nil
}

// InitialMigration adds the db_version bucket with the sole entry
func InitialMigration(db *bolt.DB) error {
	return db.Update(func(tx *bolt.Tx) error {
		return setVersion(tx, 1)
	})
}
<commit_msg>Refactor away manual management of database version increases<commit_after>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/boltdb/bolt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"time"
)

var (
	errNoMigrationsYet = errors.New("no migrations have been added yet")
	levelKey           = []byte("level")
)

// migrations is a list of all the migration functions.
// Their indexes are used to determine when they are to be applied.
//
// When adding a new migration, make sure to add it to this list.
var migrations = []func(db *bolt.DB) error{
	InitialMigration,
	MigrateOriginalColorPreferredColor,
	MigrateTournamentRunnerupStringPerson,
	MigrateMatchScoreOrder,
}

// Migrate is the main migration entrypoint
//
// When called, it will check the database to see what migrations have already
// been applied. If that is lower than the const TopVersion, all migrations up
// to that point will sequentially be applied.
func Migrate(db *bolt.DB) error {
	version, err := getVersion(db)

	if err == errNoMigrationsYet {
		// No migrations have been done yet, so lets do the first one by adding
		// the migration bucket.
		version = 0
	} else if err != nil {
		// Something actually went wrong. Oh noes.
		return err
	}

	// If version is lower than the latest known version, it's time to apply the
	// migrations!
	if version < len(migrations) {
		if err := applyMigrations(db, version); err != nil {
			log.Print("Error: Migration application failed ;'(")
			return err
		}
	} else {
		log.Print("Database up to date.")
	}

	return nil
}

func applyMigrations(db *bolt.DB, version int) error {
	log.Printf(" --- Migrating %d -> %d:\n", version, len(migrations))
	if err := backup(db, version, "db-migration-backup/"); err != nil {
		return err
	}

	// Run the new migrations and the new migrations only
	new := migrations[version:]
	for x, migration := range new {
		log.Printf("Applying migration %d\n", x+len(new))
		if err := migration(db); err != nil {
			log.Print("Migration failure: ", err)
			return err
		}
	}

	log.Printf(" --- Migrations applied successfully. <3\n")
	return nil
}

func backup(db *bolt.DB, version int, path string) error {
	_ = os.Mkdir(path, 0755)

	fn := fmt.Sprintf(
		"%d_from-v%d-to-v%d.db",
		time.Now().UnixNano(),
		version,
		len(migrations),
	)
	dst := filepath.Join(path, fn)

	data, _ := ioutil.ReadFile(db.Path())
	if err := ioutil.WriteFile(dst, data, 0644); err != nil {
		return err
	}

	log.Printf("Backed up into %s\n", fn)
	return nil
}

func getVersion(db *bolt.DB) (int, error) {
	var version int
	err := db.View(func(tx *bolt.Tx) error {
		var err error
		b := tx.Bucket(MigrationKey)
		if b == nil {
			return errNoMigrationsYet
		}

		x := b.Get(levelKey)
		version, err = strconv.Atoi(string(x))
		if err != nil {
			return err
		}

		return nil
	})

	return version, err
}

func setVersion(tx *bolt.Tx, version int) error {
	b, err := tx.CreateBucketIfNotExists(MigrationKey)
	if err != nil {
		return err
	}

	out, _ := json.Marshal(version)
	if err := b.Put(levelKey, out); err != nil {
		return err
	}
	return nil
}

// InitialMigration adds the db_version bucket with the sole entry
func InitialMigration(db *bolt.DB) error {
	return db.Update(func(tx *bolt.Tx) error {
		return setVersion(tx, 1)
	})
}
<|endoftext|>


<commit_before>package migration

import (
	"database/sql"
	"errors"
	"io/ioutil"
	"log"
	"time"
)

type Configuration struct {
	Project   string // Project id for this project
	Directory string // Directory containing the migrations files
	TableName string // Migration table name
}

type migrationFile struct {
	Filename      string
	MigrationDate time.Time
}

// Upgrade database using the the given database connection and read the
// migration sql files from "migrations/" directory
func Upgrade(db *sql.DB, project string) error {
	config := &Configuration{
		Project:   project,
		Directory: "migrations/",
		TableName: "migration_tbl",
	}

	return UpgradeDir(db, config)
}

// Upgrade database using the the given database connection and read the
// migration sql files from the given directory
func UpgradeDir(db *sql.DB, config *Configuration) error {
	log.Println("*** Migration started ***")

	err := doUpgrade(db, config)
	if err != nil {
		return err
	}

	log.Println("*** Migration ended ***")

	return nil
}

func createMigrationTable(db *sql.DB, config *Configuration) error {
	log.Println("Creating migration table")
	_, err := db.Exec("CREATE TABLE " + config.TableName + " (project varchar, filename varchar, migration_date timestamp with time zone DEFAULT now(), CONSTRAINT " + config.TableName + "_pk PRIMARY KEY (project, filename));")

	return err
}

func getInsertedFiles(db *sql.DB, config *Configuration) (map[string]time.Time, error) {

	var files map[string]time.Time = make(map[string]time.Time)

	_, err := db.Exec("SELECT * FROM " + config.TableName + " ORDER BY filename")
	if err != nil {
		err := createMigrationTable(db, config)
		if err != nil {
			return nil, err
		}
	} else {

		rows, err := db.Query("SELECT * FROM " + config.TableName + " ORDER BY filename")
		if err != nil {
			return nil, errors.New("Error fetching already migrated files")
		}

		var file migrationFile

		for rows.Next() {
			err := rows.Scan(&file.Filename, &file.MigrationDate)
			if err != nil {
				log.Fatal(err)
			}

			files[file.Filename] = file.MigrationDate
		}
	}

	return files, nil
}

func doUpgrade(db *sql.DB, config *Configuration) error {

	insertedFiles, err := getInsertedFiles(db, config)
	if err != nil {
		return err
	}

	files, err := ioutil.ReadDir(config.Directory)
	if err != nil {
		return errors.New("Error while opening migration directory")
	}

	// Start new transaction
	transaction, err := db.Begin()

	if err != nil {
		return errors.New("Cannot start migration transaction")
	}

	for _, file := range files {

		// Skip files that have already been migrated
		if _, ok := insertedFiles[file.Name()]; ok {
			continue
		}

		filename := config.Directory + file.Name()
		content, err := ioutil.ReadFile(filename)
		if err != nil {
			transaction.Rollback()
			return errors.New("Error reading " + filename + ": " + err.Error())
		}

		log.Println("Executing", file.Name())
		_, err = transaction.Exec(string(content))
		if err != nil {
			transaction.Rollback()
			return errors.New("Error in migration: " + err.Error())
		}

		// Insert file to migration table
		_, err = transaction.Exec("INSERT INTO "+config.TableName+" (project, filename) VALUES ($1, $2)", config.Project, file.Name())
		if err != nil {
			transaction.Rollback()
			return errors.New("Error while inserting file to migration table: " + err.Error())
		}
	}

	transaction.Commit()

	return nil
}
<commit_msg>Add missing project variable to select query scan<commit_after>package migration

import (
	"database/sql"
	"errors"
	"io/ioutil"
	"log"
	"time"
)

type Configuration struct {
	Project   string // Project id for this project
	Directory string // Directory containing the migrations files
	TableName string // Migration table name
}

type migrationFile struct {
	Project       string
	Filename      string
	MigrationDate time.Time
}

// Upgrade database using the the given database connection and read the
// migration sql files from "migrations/" directory
func Upgrade(db *sql.DB, project string) error {
	config := &Configuration{
		Project:   project,
		Directory: "migrations/",
		TableName: "migration_tbl",
	}

	return UpgradeDir(db, config)
}

// Upgrade database using the the given database connection and read the
// migration sql files from the given directory
func UpgradeDir(db *sql.DB, config *Configuration) error {
	log.Println("*** Migration started ***")

	err := doUpgrade(db, config)
	if err != nil {
		return err
	}

	log.Println("*** Migration ended ***")

	return nil
}

func createMigrationTable(db *sql.DB, config *Configuration) error {
	log.Println("Creating migration table")
	_, err := db.Exec("CREATE TABLE " + config.TableName + " (project varchar, filename varchar, migration_date timestamp with time zone DEFAULT now(), CONSTRAINT " + config.TableName + "_pk PRIMARY KEY (project, filename));")

	return err
}

func getInsertedFiles(db *sql.DB, config *Configuration) (map[string]time.Time, error) {

	var files map[string]time.Time = make(map[string]time.Time)

	_, err := db.Exec("SELECT * FROM " + config.TableName + " ORDER BY filename")
	if err != nil {
		err := createMigrationTable(db, config)
		if err != nil {
			return nil, err
		}
	} else {

		rows, err := db.Query("SELECT * FROM " + config.TableName + " ORDER BY filename")
		if err != nil {
			return nil, errors.New("Error fetching already migrated files")
		}

		var file migrationFile

		for rows.Next() {
			err := rows.Scan(&file.Project, &file.Filename, &file.MigrationDate)
			if err != nil {
				log.Fatal(err)
			}

			files[file.Filename] = file.MigrationDate
		}
	}

	return files, nil
}

func doUpgrade(db *sql.DB, config *Configuration) error {

	insertedFiles, err := getInsertedFiles(db, config)
	if err != nil {
		return err
	}

	files, err := ioutil.ReadDir(config.Directory)
	if err != nil {
		return errors.New("Error while opening migration directory")
	}

	// Start new transaction
	transaction, err := db.Begin()

	if err != nil {
		return errors.New("Cannot start migration transaction")
	}

	for _, file := range files {

		// Skip files that have already been migrated
		if _, ok := insertedFiles[file.Name()]; ok {
			continue
		}

		filename := config.Directory + file.Name()
		content, err := ioutil.ReadFile(filename)
		if err != nil {
			transaction.Rollback()
			return errors.New("Error reading " + filename + ": " + err.Error())
		}

		log.Println("Executing", file.Name())
		_, err = transaction.Exec(string(content))
		if err != nil {
			transaction.Rollback()
			return errors.New("Error in migration: " + err.Error())
		}

		// Insert file to migration table
		_, err = transaction.Exec("INSERT INTO "+config.TableName+" (project, filename) VALUES ($1, $2)", config.Project, file.Name())
		if err != nil {
			transaction.Rollback()
			return errors.New("Error while inserting file to migration table: " + err.Error())
		}
	}

	transaction.Commit()

	return nil
}
<|endoftext|>


<commit_before>package workerpool

import (
	"context"
	"fmt"
	"testing"
	"time"
)

func TestWorkerPool_New_NilWorkerFunc(t *testing.T) {
	wp, err := New(nil, 10)

	if wp != nil {
		t.Errorf("Expected nil WorkerPool.")
	}

	if err != ErrNilWorkerFunc {
		t.Errorf("Expected ErrNilWorkerFunc error. Got %q.", err)

	}
}

func TestWorkerPool_New_InvalidNumWorkers(t *testing.T) {
	wp, err := New(
		func(interface{}, context.Context) (interface{}, error) {
			return nil, nil
		}, 0)

	if wp != nil {
		t.Errorf("Expected nil WorkerPool.")
	}

	if err != ErrInvalidNumWorkers {
		t.Errorf("Expected ErrInvalidNumWorkers error. Got %q.", err)

	}
}

func TestWorkerPool_New_Success(t *testing.T) {
	wp, err := New(
		func(interface{}, context.Context) (interface{}, error) {
			return nil, nil
		}, 1)

	if wp == nil {
		t.Errorf("Expected non-nil WorkerPool.")
	}

	if err != nil {
		t.Errorf("Expected nil error. Got %q.", err)
	}
}

func TestWorkerPool_GetOutputChannel(t *testing.T) {
	wp, _ := New(
		func(interface{}, context.Context) (interface{}, error) {
			return nil, nil
		}, 1)

	if wp.outputChannel != nil {
		t.Errorf("Internal channel was not nil before " +
			"GetOutputChannel().")
	}

	c := wp.GetOutputChannel()

	if c != wp.outputChannel {
		t.Errorf("Internal and returned channels do not match.")
	}
}
func TestWorkerPool_SetInputChannel_NilInputChannel(t *testing.T) {
	wp, _ := New(
		func(interface{}, context.Context) (interface{}, error) {
			return nil, nil
		}, 1)

	if wp.inputChannel != nil {
		t.Errorf("Internal input channel was not nil before " +
			"SetInputChannel().")
	}

	err := wp.SetInputChannel(nil)
	if err != ErrNilInputChannel {
		t.Errorf("Expected ErrNilInputChannel error. Got %q.", err)
	}
}

func TestWorkerPool_SetInputChannel(t *testing.T) {
	wp, _ := New(
		func(interface{}, context.Context) (interface{}, error) {
			return nil, nil
		}, 1)

	if wp.inputChannel != nil {
		t.Errorf("Internal input channel was not nil before " +
			"SetInputChannel().")
	}

	c := make(chan interface{})

	err := wp.SetInputChannel(c)
	if err != nil {
		t.Errorf("Expected nil error. Got %q.", err)
	}

	if c != wp.inputChannel {
		t.Errorf("Internal and given channels do not match.")
	}
}

func TestWorkerPool_Start_NilOutputChannel(t *testing.T) {
	wp, _ := New(
		func(interface{}, context.Context) (interface{}, error) {
			return nil, nil
		}, 1)
	_ = wp.SetInputChannel(make(chan interface{}))

	err := wp.Start(context.Background())
	if err != ErrNilOutputChannel {
		t.Errorf("Expected ErrNewOutputChannel error. Got %q.", err)
	}
}

func TestWorkerPool_Start_AlreadyStarted(t *testing.T) {
	wp, _ := New(
		func(interface{}, context.Context) (interface{}, error) {
			return nil, nil
		}, 1)
	_ = wp.SetInputChannel(make(chan interface{}))
	_ = wp.GetOutputChannel()

	// Do not leak goroutines.
	ctx, cancelFunc := context.WithCancel(context.Background())
	defer cancelFunc()

	_ = wp.Start(ctx)

	err := wp.Start(ctx)
	if err != ErrAlreadyStarted {
		t.Errorf("Expected ErrAlreadyStarted error. Got %q.", err)
	}
}

func TestWorkerPool_Wait_NotStarted(t *testing.T) {
	wp, _ := New(
		func(interface{}, context.Context) (interface{}, error) {
			return nil, nil
		}, 1)
	_ = wp.SetInputChannel(make(chan interface{}))
	_ = wp.GetOutputChannel()

	err := wp.Wait()
	if err != ErrNotStarted {
		t.Errorf("Expected ErrNotStarted error. Got %q.", err)
	}
}

func TestWorkerPool_Wait_Success(t *testing.T) {
	wp, _ := New(
		func(interface{}, context.Context) (interface{}, error) {
			return nil, nil
		}, 1)
	_ = wp.SetInputChannel(make(chan interface{}))
	_ = wp.GetOutputChannel()

	ctx, cancelFunc := context.WithTimeout(context.Background(), 1*time.Millisecond)
	defer cancelFunc()

	_ = wp.Start(ctx)

	err := wp.Wait()
	if err != context.DeadlineExceeded {
		t.Errorf("Expected context.DeadlineExceeded error. Got %q.",
			err)
	}
}

func TestWorkerPool_CleanupOnCancel(t *testing.T) {
	wp, _ := New(
		func(interface{}, context.Context) (interface{}, error) {
			return nil, nil
		}, 1)
	_ = wp.SetInputChannel(make(chan interface{}))
	_ = wp.GetOutputChannel()

	ctx, cancelFunc := context.WithCancel(context.Background())

	_ = wp.Start(ctx)

	cancelFunc()

	err := wp.Wait()
	if err != context.Canceled {
		t.Errorf("Expected context.Canceled error. Got %q.", err)
	}

	// We are going to look directly at the WorkerPool attributes, so we
	// have to Lock to avoid data races.
	wp.m.Lock()

	if wp.started {
		t.Errorf("Expected worker pool to be stopped.")
	}

	if wp.outputChannel != nil {
		t.Errorf("Expected outputChannel to be nil.")
	}

	wp.m.Unlock()
}

func TestWorkerPool_WorkerFuncError(t *testing.T) {
	wp, _ := New(
		func(interface{}, context.Context) (interface{}, error) {
			return nil, fmt.Errorf("error test")
		}, 1)

	ic := make(chan interface{})
	_ = wp.SetInputChannel(ic)

	oc := wp.GetOutputChannel()

	_ = wp.Start(context.Background())

	go func() {
		result := <-oc

		we, ok := result.(WorkerError)
		if !ok {
			t.Errorf("Expected WorkerError. Got %t.", result)
		}

		if we.Error.Error() != "error test" {
			t.Errorf("Expected error \"error test\". Got %q.",
				we.Error.Error())
		}
	}()

	ic <- struct{}{}

	// Clean shutdown.
	close(ic)

	wp.Wait()
}

func TestWorkerPool_WorkerFuncSuccess(t *testing.T) {
	wp, _ := New(
		func(interface{}, context.Context) (interface{}, error) {
			return "test result", nil
		}, 1)

	ic := make(chan interface{})
	_ = wp.SetInputChannel(ic)

	oc := wp.GetOutputChannel()

	_ = wp.Start(context.Background())

	go func() {
		result := <-oc

		r, ok := result.(string)
		if !ok {
			t.Errorf("Expected string. Got %t.", result)
		}

		if r != "test result" {
			t.Errorf("Expected result \"test result\". Got %q.", r)
		}
	}()

	ic <- struct{}{}

	// Clean shutdown.
	close(ic)

	err := wp.Wait()
	if err != nil {
		t.Errorf("Expected nil error. Got %q.", err)
	}
}
<commit_msg>Bring test coverage to 100%.<commit_after>package workerpool

import (
	"context"
	"fmt"
	"testing"
	"time"
)

func TestWorkerPool_New_NilWorkerFunc(t *testing.T) {
	wp, err := New(nil, 10)

	if wp != nil {
		t.Errorf("Expected nil WorkerPool.")
	}

	if err != ErrNilWorkerFunc {
		t.Errorf("Expected ErrNilWorkerFunc error. Got %q.", err)

	}
}

func TestWorkerPool_New_InvalidNumWorkers(t *testing.T) {
	wp, err := New(
		func(interface{}, context.Context) (interface{}, error) {
			return nil, nil
		}, 0)

	if wp != nil {
		t.Errorf("Expected nil WorkerPool.")
	}

	if err != ErrInvalidNumWorkers {
		t.Errorf("Expected ErrInvalidNumWorkers error. Got %q.", err)

	}
}

func TestWorkerPool_New_Success(t *testing.T) {
	wp, err := New(
		func(interface{}, context.Context) (interface{}, error) {
			return nil, nil
		}, 1)

	if wp == nil {
		t.Errorf("Expected non-nil WorkerPool.")
	}

	if err != nil {
		t.Errorf("Expected nil error. Got %q.", err)
	}
}

func TestWorkerPool_GetOutputChannel(t *testing.T) {
	wp, _ := New(
		func(interface{}, context.Context) (interface{}, error) {
			return nil, nil
		}, 1)

	if wp.outputChannel != nil {
		t.Errorf("Internal channel was not nil before " +
			"GetOutputChannel().")
	}

	c := wp.GetOutputChannel()

	if c != wp.outputChannel {
		t.Errorf("Internal and returned channels do not match.")
	}
}
func TestWorkerPool_SetInputChannel_NilInputChannel(t *testing.T) {
	wp, _ := New(
		func(interface{}, context.Context) (interface{}, error) {
			return nil, nil
		}, 1)

	if wp.inputChannel != nil {
		t.Errorf("Internal input channel was not nil before " +
			"SetInputChannel().")
	}

	err := wp.SetInputChannel(nil)
	if err != ErrNilInputChannel {
		t.Errorf("Expected ErrNilInputChannel error. Got %q.", err)
	}
}

func TestWorkerPool_SetInputChannel(t *testing.T) {
	wp, _ := New(
		func(interface{}, context.Context) (interface{}, error) {
			return nil, nil
		}, 1)

	if wp.inputChannel != nil {
		t.Errorf("Internal input channel was not nil before " +
			"SetInputChannel().")
	}

	c := make(chan interface{})

	err := wp.SetInputChannel(c)
	if err != nil {
		t.Errorf("Expected nil error. Got %q.", err)
	}

	if c != wp.inputChannel {
		t.Errorf("Internal and given channels do not match.")
	}
}

func TestWorkerPool_Start_NilOutputChannel(t *testing.T) {
	wp, _ := New(
		func(interface{}, context.Context) (interface{}, error) {
			return nil, nil
		}, 1)
	_ = wp.SetInputChannel(make(chan interface{}))

	err := wp.Start(context.Background())
	if err != ErrNilOutputChannel {
		t.Errorf("Expected ErrNewOutputChannel error. Got %q.", err)
	}
}

func TestWorkerPool_Start_NilInputChannel(t *testing.T) {
	wp, _ := New(
		func(interface{}, context.Context) (interface{}, error) {
			return nil, nil
		}, 1)
	_ = wp.GetOutputChannel()

	err := wp.Start(context.Background())
	if err != ErrNilInputChannel {
		t.Errorf("Expected ErrNewInputChannel error. Got %q.", err)
	}
}

func TestWorkerPool_Start_AlreadyStarted(t *testing.T) {
	wp, _ := New(
		func(interface{}, context.Context) (interface{}, error) {
			return nil, nil
		}, 1)
	_ = wp.SetInputChannel(make(chan interface{}))
	_ = wp.GetOutputChannel()

	// Do not leak goroutines.
	ctx, cancelFunc := context.WithCancel(context.Background())
	defer cancelFunc()

	_ = wp.Start(ctx)

	err := wp.Start(ctx)
	if err != ErrAlreadyStarted {
		t.Errorf("Expected ErrAlreadyStarted error. Got %q.", err)
	}
}

func TestWorkerPool_Wait_NotStarted(t *testing.T) {
	wp, _ := New(
		func(interface{}, context.Context) (interface{}, error) {
			return nil, nil
		}, 1)
	_ = wp.SetInputChannel(make(chan interface{}))
	_ = wp.GetOutputChannel()

	err := wp.Wait()
	if err != ErrNotStarted {
		t.Errorf("Expected ErrNotStarted error. Got %q.", err)
	}
}

func TestWorkerPool_Wait_Success(t *testing.T) {
	wp, _ := New(
		func(interface{}, context.Context) (interface{}, error) {
			return nil, nil
		}, 1)
	_ = wp.SetInputChannel(make(chan interface{}))
	_ = wp.GetOutputChannel()

	ctx, cancelFunc := context.WithTimeout(context.Background(), 1*time.Millisecond)
	defer cancelFunc()

	_ = wp.Start(ctx)

	err := wp.Wait()
	if err != context.DeadlineExceeded {
		t.Errorf("Expected context.DeadlineExceeded error. Got %q.",
			err)
	}
}

func TestWorkerPool_CleanupOnCancel(t *testing.T) {
	wp, _ := New(
		func(interface{}, context.Context) (interface{}, error) {
			return nil, nil
		}, 1)
	_ = wp.SetInputChannel(make(chan interface{}))
	_ = wp.GetOutputChannel()

	ctx, cancelFunc := context.WithCancel(context.Background())

	_ = wp.Start(ctx)

	cancelFunc()

	err := wp.Wait()
	if err != context.Canceled {
		t.Errorf("Expected context.Canceled error. Got %q.", err)
	}

	// We are going to look directly at the WorkerPool attributes, so we
	// have to Lock to avoid data races.
	wp.m.Lock()

	if wp.started {
		t.Errorf("Expected worker pool to be stopped.")
	}

	if wp.outputChannel != nil {
		t.Errorf("Expected outputChannel to be nil.")
	}

	wp.m.Unlock()
}

func TestWorkerPool_WorkerFuncError(t *testing.T) {
	wp, _ := New(
		func(interface{}, context.Context) (interface{}, error) {
			return nil, fmt.Errorf("error test")
		}, 1)

	ic := make(chan interface{})
	_ = wp.SetInputChannel(ic)

	oc := wp.GetOutputChannel()

	_ = wp.Start(context.Background())

	go func() {
		result := <-oc

		we, ok := result.(WorkerError)
		if !ok {
			t.Errorf("Expected WorkerError. Got %t.", result)
		}

		if we.Error.Error() != "error test" {
			t.Errorf("Expected error \"error test\". Got %q.",
				we.Error.Error())
		}
	}()

	ic <- struct{}{}

	// Clean shutdown.
	close(ic)

	wp.Wait()
}

func TestWorkerPool_WorkerFuncSuccess(t *testing.T) {
	wp, _ := New(
		func(interface{}, context.Context) (interface{}, error) {
			return "test result", nil
		}, 1)

	ic := make(chan interface{})
	_ = wp.SetInputChannel(ic)

	oc := wp.GetOutputChannel()

	_ = wp.Start(context.Background())

	go func() {
		result := <-oc

		r, ok := result.(string)
		if !ok {
			t.Errorf("Expected string. Got %t.", result)
		}

		if r != "test result" {
			t.Errorf("Expected result \"test result\". Got %q.", r)
		}
	}()

	ic <- struct{}{}

	// Clean shutdown.
	close(ic)

	err := wp.Wait()
	if err != nil {
		t.Errorf("Expected nil error. Got %q.", err)
	}
}
<|endoftext|>


<commit_before>package verbalexpressions

/* proxy and helpers to regexp.Regexp functions */

// Test return true if verbalexpressions matches something in string "s"
func (v *VerbalExpression) Test(s string) bool {
	return v.Regex().Match([]byte(s))
}

// Replace alias to regexp.ReplaceAllString. It replace the found expression from
// string src by string dst
func (v *VerbalExpression) Replace(src string, dst string) string {
	return v.Regex().ReplaceAllString(src, dst)
}

// Returns a slice of results from captures. If you didn't apply BeginCapture() and EnCapture(), the slices
// will return slice of []string where []string is length 1, and 0 index is the global capture
func (v *VerbalExpression) Captures(s string) [][]string {
	iter := 1
	if v.flags&GLOBAL != 0 {
		iter = -1
	}
	return v.Regex().FindAllStringSubmatch(s, iter)
}
<commit_msg>Fix small typo in helpers.go<commit_after>package verbalexpressions

/* proxy and helpers to regexp.Regexp functions */

// Test return true if verbalexpressions matches something in string "s"
func (v *VerbalExpression) Test(s string) bool {
	return v.Regex().Match([]byte(s))
}

// Replace alias to regexp.ReplaceAllString. It replace the found expression from
// string src by string dst
func (v *VerbalExpression) Replace(src string, dst string) string {
	return v.Regex().ReplaceAllString(src, dst)
}

// Returns a slice of results from captures. If you didn't apply BeginCapture() and EndCapture(), the slices
// will return slice of []string where []string is length 1, and 0 index is the global capture
func (v *VerbalExpression) Captures(s string) [][]string {
	iter := 1
	if v.flags&GLOBAL != 0 {
		iter = -1
	}
	return v.Regex().FindAllStringSubmatch(s, iter)
}
<|endoftext|>


<commit_before>package script

import (
	"fmt"
	"github.com/tyler-sommer/squircy2/squircy/config"
	"github.com/tyler-sommer/squircy2/squircy/event"
	"github.com/tyler-sommer/squircy2/squircy/irc"
	"github.com/tyler-sommer/squircy2/squircy/sysinfo"
	"io/ioutil"
	"net/http"
)

type httpHelper struct{}

func (client *httpHelper) Get(url string) string {
	resp, err := http.Get(url)
	if err != nil {
		return ""
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return ""
	}
	return string(body)
}

type configHelper struct {
	conf *config.Configuration
}

func (h *configHelper) OwnerNick() string {
	return h.conf.OwnerNick
}

func (h *configHelper) OwnerHost() string {
	return h.conf.OwnerHost
}

type dataHelper struct {
	d map[string]interface{}
}

func (db *dataHelper) Get(key string) interface{} {
	if val, ok := db.d[key]; ok {
		return val
	}

	return nil
}

func (db *dataHelper) Set(key string, val interface{}) {
	db.d[key] = val
}

type ircHelper struct {
	manager *irc.IrcConnectionManager
}

func (h *ircHelper) Privmsg(target, message string) {
	conn := h.manager.Connection()
	if conn == nil {
		return
	}
	conn.Privmsg(target, message)
}

func (h *ircHelper) Join(target string) {
	conn := h.manager.Connection()
	if conn == nil {
		return
	}
	conn.Join(target)
}

func (h *ircHelper) Part(target string) {
	conn := h.manager.Connection()
	if conn == nil {
		return
	}
	conn.Part(target)
}

func (h *ircHelper) CurrentNick() string {
	conn := h.manager.Connection()
	if conn == nil {
		return ""
	}
	return conn.GetNick()
}

func (h *ircHelper) Nick(newNick string) {
	conn := h.manager.Connection()
	if conn == nil {
		return
	}
	conn.Nick(newNick)
}

type scriptHelper struct {
	e          event.EventManager
	jsDriver   javascriptDriver
	handlers   map[string]event.EventHandler
}

func handlerId(scriptType ScriptType, eventType event.EventType, fnName string) string {
	return fmt.Sprintf("%v-%v-%v", scriptType, eventType, fnName)
}

// Bind adds a handler of the given script type for the given event type
func (s *scriptHelper) Bind(scriptType ScriptType, eventType event.EventType, fnName string) {
	id := handlerId(scriptType, eventType, fnName)
	var d scriptDriver
	switch {
	case scriptType == Javascript:
		d = s.jsDriver
	}

	handler := func(ev event.Event) {
		d.Handle(ev, fnName)
	}
	s.handlers[id] = handler
	s.e.Bind(eventType, handler)
}

// Unbind removes a handler of the given script type for the given event type
func (s *scriptHelper) Unbind(scriptType ScriptType, eventType event.EventType, fnName string) {
	id := handlerId(scriptType, eventType, fnName)
	handler, ok := s.handlers[id]
	if !ok {
		return
	}
	s.e.Unbind(eventType, handler)
	delete(s.handlers, id)
}

func (s *scriptHelper) Trigger(eventType event.EventType, data map[string]interface{}) {
	s.e.Trigger(eventType, data)
}

type osHelper struct {}

func (h *osHelper) SystemInfo() sysinfo.SystemInfo {
	return sysinfo.New()
}
<commit_msg>Add Raw method to Irc module in JS VM<commit_after>package script

import (
	"fmt"
	"github.com/tyler-sommer/squircy2/squircy/config"
	"github.com/tyler-sommer/squircy2/squircy/event"
	"github.com/tyler-sommer/squircy2/squircy/irc"
	"github.com/tyler-sommer/squircy2/squircy/sysinfo"
	"io/ioutil"
	"net/http"
)

type httpHelper struct{}

func (client *httpHelper) Get(url string) string {
	resp, err := http.Get(url)
	if err != nil {
		return ""
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return ""
	}
	return string(body)
}

type configHelper struct {
	conf *config.Configuration
}

func (h *configHelper) OwnerNick() string {
	return h.conf.OwnerNick
}

func (h *configHelper) OwnerHost() string {
	return h.conf.OwnerHost
}

type dataHelper struct {
	d map[string]interface{}
}

func (db *dataHelper) Get(key string) interface{} {
	if val, ok := db.d[key]; ok {
		return val
	}

	return nil
}

func (db *dataHelper) Set(key string, val interface{}) {
	db.d[key] = val
}

type ircHelper struct {
	manager *irc.IrcConnectionManager
}

func (h *ircHelper) Privmsg(target, message string) {
	conn := h.manager.Connection()
	if conn == nil {
		return
	}
	conn.Privmsg(target, message)
}

func (h *ircHelper) Join(target string) {
	conn := h.manager.Connection()
	if conn == nil {
		return
	}
	conn.Join(target)
}

func (h *ircHelper) Part(target string) {
	conn := h.manager.Connection()
	if conn == nil {
		return
	}
	conn.Part(target)
}

func (h *ircHelper) CurrentNick() string {
	conn := h.manager.Connection()
	if conn == nil {
		return ""
	}
	return conn.GetNick()
}

func (h *ircHelper) Nick(newNick string) {
	conn := h.manager.Connection()
	if conn == nil {
		return
	}
	conn.Nick(newNick)
}

func (h *ircHelper) Raw(command string) {
	conn := h.manager.Connection()
	if conn == nil {
		return
	}
	conn.SendRaw(command)
}

type scriptHelper struct {
	e          event.EventManager
	jsDriver   javascriptDriver
	handlers   map[string]event.EventHandler
}

func handlerId(scriptType ScriptType, eventType event.EventType, fnName string) string {
	return fmt.Sprintf("%v-%v-%v", scriptType, eventType, fnName)
}

// Bind adds a handler of the given script type for the given event type
func (s *scriptHelper) Bind(scriptType ScriptType, eventType event.EventType, fnName string) {
	id := handlerId(scriptType, eventType, fnName)
	var d scriptDriver
	switch {
	case scriptType == Javascript:
		d = s.jsDriver
	}

	handler := func(ev event.Event) {
		d.Handle(ev, fnName)
	}
	s.handlers[id] = handler
	s.e.Bind(eventType, handler)
}

// Unbind removes a handler of the given script type for the given event type
func (s *scriptHelper) Unbind(scriptType ScriptType, eventType event.EventType, fnName string) {
	id := handlerId(scriptType, eventType, fnName)
	handler, ok := s.handlers[id]
	if !ok {
		return
	}
	s.e.Unbind(eventType, handler)
	delete(s.handlers, id)
}

func (s *scriptHelper) Trigger(eventType event.EventType, data map[string]interface{}) {
	s.e.Trigger(eventType, data)
}

type osHelper struct {}

func (h *osHelper) SystemInfo() sysinfo.SystemInfo {
	return sysinfo.New()
}
<|endoftext|>


<commit_before>package api

import (
	"crypto/tls"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"

	"golang.org/x/net/http2"

	"github.com/hashicorp/go-cleanhttp"
	"github.com/hashicorp/go-rootcerts"
	"github.com/sethgrid/pester"
)

const EnvVaultAddress = "VAULT_ADDR"
const EnvVaultCACert = "VAULT_CACERT"
const EnvVaultCAPath = "VAULT_CAPATH"
const EnvVaultClientCert = "VAULT_CLIENT_CERT"
const EnvVaultClientKey = "VAULT_CLIENT_KEY"
const EnvVaultInsecure = "VAULT_SKIP_VERIFY"
const EnvVaultTLSServerName = "VAULT_TLS_SERVER_NAME"
const EnvVaultWrapTTL = "VAULT_WRAP_TTL"
const EnvVaultMaxRetries = "VAULT_MAX_RETRIES"
const EnvVaultToken = "VAULT_TOKEN"

// WrappingLookupFunc is a function that, given an HTTP verb and a path,
// returns an optional string duration to be used for response wrapping (e.g.
// "15s", or simply "15"). The path will not begin with "/v1/" or "v1/" or "/",
// however, end-of-path forward slashes are not trimmed, so must match your
// called path precisely.
type WrappingLookupFunc func(operation, path string) string

// Config is used to configure the creation of the client.
type Config struct {
	// Address is the address of the Vault server. This should be a complete
	// URL such as "http://vault.example.com". If you need a custom SSL
	// cert or want to enable insecure mode, you need to specify a custom
	// HttpClient.
	Address string

	// HttpClient is the HTTP client to use, which will currently always have the
	// same values as http.DefaultClient. This is used to control redirect behavior.
	HttpClient *http.Client

	redirectSetup sync.Once

	// MaxRetries controls the maximum number of times to retry when a 5xx error
	// occurs. Set to 0 or less to disable retrying. Defaults to 0.
	MaxRetries int
}

// TLSConfig contains the parameters needed to configure TLS on the HTTP client
// used to communicate with Vault.
type TLSConfig struct {
	// CACert is the path to a PEM-encoded CA cert file to use to verify the
	// Vault server SSL certificate.
	CACert string

	// CAPath is the path to a directory of PEM-encoded CA cert files to verify
	// the Vault server SSL certificate.
	CAPath string

	// ClientCert is the path to the certificate for Vault communication
	ClientCert string

	// ClientKey is the path to the private key for Vault communication
	ClientKey string

	// TLSServerName, if set, is used to set the SNI host when connecting via
	// TLS.
	TLSServerName string

	// Insecure enables or disables SSL verification
	Insecure bool
}

// DefaultConfig returns a default configuration for the client. It is
// safe to modify the return value of this function.
//
// The default Address is https://127.0.0.1:8200, but this can be overridden by
// setting the `VAULT_ADDR` environment variable.
func DefaultConfig() *Config {
	config := &Config{
		Address:    "https://127.0.0.1:8200",
		HttpClient: cleanhttp.DefaultClient(),
	}
	config.HttpClient.Timeout = time.Second * 60
	transport := config.HttpClient.Transport.(*http.Transport)
	transport.TLSHandshakeTimeout = 10 * time.Second
	transport.TLSClientConfig = &tls.Config{
		MinVersion: tls.VersionTLS12,
	}

	if v := os.Getenv(EnvVaultAddress); v != "" {
		config.Address = v
	}

	return config
}

// ConfigureTLS takes a set of TLS configurations and applies those to the the HTTP client.
func (c *Config) ConfigureTLS(t *TLSConfig) error {
	if c.HttpClient == nil {
		c.HttpClient = DefaultConfig().HttpClient
	}

	var clientCert tls.Certificate
	foundClientCert := false
	if t.CACert != "" || t.CAPath != "" || t.ClientCert != "" || t.ClientKey != "" || t.Insecure {
		if t.ClientCert != "" && t.ClientKey != "" {
			var err error
			clientCert, err = tls.LoadX509KeyPair(t.ClientCert, t.ClientKey)
			if err != nil {
				return err
			}
			foundClientCert = true
		} else if t.ClientCert != "" || t.ClientKey != "" {
			return fmt.Errorf("Both client cert and client key must be provided")
		}
	}

	clientTLSConfig := c.HttpClient.Transport.(*http.Transport).TLSClientConfig
	rootConfig := &rootcerts.Config{
		CAFile: t.CACert,
		CAPath: t.CAPath,
	}
	if err := rootcerts.ConfigureTLS(clientTLSConfig, rootConfig); err != nil {
		return err
	}

	clientTLSConfig.InsecureSkipVerify = t.Insecure

	if foundClientCert {
		clientTLSConfig.Certificates = []tls.Certificate{clientCert}
	}
	if t.TLSServerName != "" {
		clientTLSConfig.ServerName = t.TLSServerName
	}

	return nil
}

// ReadEnvironment reads configuration information from the
// environment. If there is an error, no configuration value
// is updated.
func (c *Config) ReadEnvironment() error {
	var envAddress string
	var envCACert string
	var envCAPath string
	var envClientCert string
	var envClientKey string
	var envInsecure bool
	var envTLSServerName string
	var envMaxRetries *uint64

	// Parse the environment variables
	if v := os.Getenv(EnvVaultAddress); v != "" {
		envAddress = v
	}
	if v := os.Getenv(EnvVaultMaxRetries); v != "" {
		maxRetries, err := strconv.ParseUint(v, 10, 32)
		if err != nil {
			return err
		}
		envMaxRetries = &maxRetries
	}
	if v := os.Getenv(EnvVaultCACert); v != "" {
		envCACert = v
	}
	if v := os.Getenv(EnvVaultCAPath); v != "" {
		envCAPath = v
	}
	if v := os.Getenv(EnvVaultClientCert); v != "" {
		envClientCert = v
	}
	if v := os.Getenv(EnvVaultClientKey); v != "" {
		envClientKey = v
	}
	if v := os.Getenv(EnvVaultInsecure); v != "" {
		var err error
		envInsecure, err = strconv.ParseBool(v)
		if err != nil {
			return fmt.Errorf("Could not parse VAULT_SKIP_VERIFY")
		}
	}
	if v := os.Getenv(EnvVaultTLSServerName); v != "" {
		envTLSServerName = v
	}

	// Configure the HTTP clients TLS configuration.
	t := &TLSConfig{
		CACert:        envCACert,
		CAPath:        envCAPath,
		ClientCert:    envClientCert,
		ClientKey:     envClientKey,
		TLSServerName: envTLSServerName,
		Insecure:      envInsecure,
	}
	if err := c.ConfigureTLS(t); err != nil {
		return err
	}

	if envAddress != "" {
		c.Address = envAddress
	}

	if envMaxRetries != nil {
		c.MaxRetries = int(*envMaxRetries) + 1
	}

	return nil
}

// Client is the client to the Vault API. Create a client with
// NewClient.
type Client struct {
	addr               *url.URL
	config             *Config
	token              string
	wrappingLookupFunc WrappingLookupFunc
}

// NewClient returns a new client for the given configuration.
//
// If the environment variable `VAULT_TOKEN` is present, the token will be
// automatically added to the client. Otherwise, you must manually call
// `SetToken()`.
func NewClient(c *Config) (*Client, error) {
	if c == nil {
		c = DefaultConfig()
		if err := c.ReadEnvironment(); err != nil {
			return nil, fmt.Errorf("error reading environment: %v", err)
		}
	}

	u, err := url.Parse(c.Address)
	if err != nil {
		return nil, err
	}

	if c.HttpClient == nil {
		c.HttpClient = DefaultConfig().HttpClient
	}

	tp := c.HttpClient.Transport.(*http.Transport)
	if err := http2.ConfigureTransport(tp); err != nil {
		return nil, err
	}

	redirFunc := func() {
		// Ensure redirects are not automatically followed
		// Note that this is sane for the API client as it has its own
		// redirect handling logic (and thus also for command/meta),
		// but in e.g. http_test actual redirect handling is necessary
		c.HttpClient.CheckRedirect = func(req *http.Request, via []*http.Request) error {
			// Returning this value causes the Go net library to not close the
			// response body and nil out the error. Otherwise pester tries
			// three times on every redirect because it sees an error from this
			// function being passed through.
			return http.ErrUseLastResponse
		}
	}

	c.redirectSetup.Do(redirFunc)

	client := &Client{
		addr:   u,
		config: c,
	}

	if token := os.Getenv(EnvVaultToken); token != "" {
		client.SetToken(token)
	}

	return client, nil
}

// Sets the address of Vault in the client. The format of address should be
// "<Scheme>://<Host>:<Port>". Setting this on a client will override the
// value of VAULT_ADDR environment variable.
func (c *Client) SetAddress(addr string) error {
	var err error
	if c.addr, err = url.Parse(addr); err != nil {
		return fmt.Errorf("failed to set address: %v", err)
	}

	return nil
}

// Address returns the Vault URL the client is configured to connect to
func (c *Client) Address() string {
	return c.addr.String()
}

// SetWrappingLookupFunc sets a lookup function that returns desired wrap TTLs
// for a given operation and path
func (c *Client) SetWrappingLookupFunc(lookupFunc WrappingLookupFunc) {
	c.wrappingLookupFunc = lookupFunc
}

// Token returns the access token being used by this client. It will
// return the empty string if there is no token set.
func (c *Client) Token() string {
	return c.token
}

// SetToken sets the token directly. This won't perform any auth
// verification, it simply sets the token properly for future requests.
func (c *Client) SetToken(v string) {
	c.token = v
}

// ClearToken deletes the token if it is set or does nothing otherwise.
func (c *Client) ClearToken() {
	c.token = ""
}

// NewRequest creates a new raw request object to query the Vault server
// configured for this client. This is an advanced method and generally
// doesn't need to be called externally.
func (c *Client) NewRequest(method, path string) *Request {
	req := &Request{
		Method: method,
		URL: &url.URL{
			Scheme: c.addr.Scheme,
			Host:   c.addr.Host,
			Path:   path,
		},
		ClientToken: c.token,
		Params:      make(map[string][]string),
	}

	var lookupPath string
	switch {
	case strings.HasPrefix(path, "/v1/"):
		lookupPath = strings.TrimPrefix(path, "/v1/")
	case strings.HasPrefix(path, "v1/"):
		lookupPath = strings.TrimPrefix(path, "v1/")
	default:
		lookupPath = path
	}
	if c.wrappingLookupFunc != nil {
		req.WrapTTL = c.wrappingLookupFunc(method, lookupPath)
	} else {
		req.WrapTTL = DefaultWrappingLookupFunc(method, lookupPath)
	}

	return req
}

// RawRequest performs the raw request given. This request may be against
// a Vault server not configured with this client. This is an advanced operation
// that generally won't need to be called externally.
func (c *Client) RawRequest(r *Request) (*Response, error) {
	redirectCount := 0
START:
	req, err := r.ToHTTP()
	if err != nil {
		return nil, err
	}

	client := pester.NewExtendedClient(c.config.HttpClient)
	client.Backoff = pester.LinearJitterBackoff
	client.MaxRetries = c.config.MaxRetries

	var result *Response
	resp, err := client.Do(req)
	if resp != nil {
		result = &Response{Response: resp}
	}
	if err != nil {
		if strings.Contains(err.Error(), "tls: oversized") {
			err = fmt.Errorf(
				"%s\n\n"+
					"This error usually means that the server is running with TLS disabled\n"+
					"but the client is configured to use TLS. Please either enable TLS\n"+
					"on the server or run the client with -address set to an address\n"+
					"that uses the http protocol:\n\n"+
					"    vault <command> -address http://<address>\n\n"+
					"You can also set the VAULT_ADDR environment variable:\n\n\n"+
					"    VAULT_ADDR=http://<address> vault <command>\n\n"+
					"where <address> is replaced by the actual address to the server.",
				err)
		}
		return result, err
	}

	// Check for a redirect, only allowing for a single redirect
	if (resp.StatusCode == 301 || resp.StatusCode == 302 || resp.StatusCode == 307) && redirectCount == 0 {
		// Parse the updated location
		respLoc, err := resp.Location()
		if err != nil {
			return result, err
		}

		// Ensure a protocol downgrade doesn't happen
		if req.URL.Scheme == "https" && respLoc.Scheme != "https" {
			return result, fmt.Errorf("redirect would cause protocol downgrade")
		}

		// Update the request
		r.URL = respLoc

		// Reset the request body if any
		if err := r.ResetJSONBody(); err != nil {
			return result, err
		}

		// Retry the request
		redirectCount++
		goto START
	}

	if err := result.Error(); err != nil {
		return result, err
	}

	return result, nil
}
<commit_msg>Add ability to set max retries to API<commit_after>package api

import (
	"crypto/tls"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"

	"golang.org/x/net/http2"

	"github.com/hashicorp/go-cleanhttp"
	"github.com/hashicorp/go-rootcerts"
	"github.com/sethgrid/pester"
)

const EnvVaultAddress = "VAULT_ADDR"
const EnvVaultCACert = "VAULT_CACERT"
const EnvVaultCAPath = "VAULT_CAPATH"
const EnvVaultClientCert = "VAULT_CLIENT_CERT"
const EnvVaultClientKey = "VAULT_CLIENT_KEY"
const EnvVaultInsecure = "VAULT_SKIP_VERIFY"
const EnvVaultTLSServerName = "VAULT_TLS_SERVER_NAME"
const EnvVaultWrapTTL = "VAULT_WRAP_TTL"
const EnvVaultMaxRetries = "VAULT_MAX_RETRIES"
const EnvVaultToken = "VAULT_TOKEN"

// WrappingLookupFunc is a function that, given an HTTP verb and a path,
// returns an optional string duration to be used for response wrapping (e.g.
// "15s", or simply "15"). The path will not begin with "/v1/" or "v1/" or "/",
// however, end-of-path forward slashes are not trimmed, so must match your
// called path precisely.
type WrappingLookupFunc func(operation, path string) string

// Config is used to configure the creation of the client.
type Config struct {
	// Address is the address of the Vault server. This should be a complete
	// URL such as "http://vault.example.com". If you need a custom SSL
	// cert or want to enable insecure mode, you need to specify a custom
	// HttpClient.
	Address string

	// HttpClient is the HTTP client to use, which will currently always have the
	// same values as http.DefaultClient. This is used to control redirect behavior.
	HttpClient *http.Client

	redirectSetup sync.Once

	// MaxRetries controls the maximum number of times to retry when a 5xx error
	// occurs. Set to 0 or less to disable retrying. Defaults to 0.
	MaxRetries int
}

// TLSConfig contains the parameters needed to configure TLS on the HTTP client
// used to communicate with Vault.
type TLSConfig struct {
	// CACert is the path to a PEM-encoded CA cert file to use to verify the
	// Vault server SSL certificate.
	CACert string

	// CAPath is the path to a directory of PEM-encoded CA cert files to verify
	// the Vault server SSL certificate.
	CAPath string

	// ClientCert is the path to the certificate for Vault communication
	ClientCert string

	// ClientKey is the path to the private key for Vault communication
	ClientKey string

	// TLSServerName, if set, is used to set the SNI host when connecting via
	// TLS.
	TLSServerName string

	// Insecure enables or disables SSL verification
	Insecure bool
}

// DefaultConfig returns a default configuration for the client. It is
// safe to modify the return value of this function.
//
// The default Address is https://127.0.0.1:8200, but this can be overridden by
// setting the `VAULT_ADDR` environment variable.
func DefaultConfig() *Config {
	config := &Config{
		Address:    "https://127.0.0.1:8200",
		HttpClient: cleanhttp.DefaultClient(),
	}
	config.HttpClient.Timeout = time.Second * 60
	transport := config.HttpClient.Transport.(*http.Transport)
	transport.TLSHandshakeTimeout = 10 * time.Second
	transport.TLSClientConfig = &tls.Config{
		MinVersion: tls.VersionTLS12,
	}

	if v := os.Getenv(EnvVaultAddress); v != "" {
		config.Address = v
	}

	return config
}

// ConfigureTLS takes a set of TLS configurations and applies those to the the HTTP client.
func (c *Config) ConfigureTLS(t *TLSConfig) error {
	if c.HttpClient == nil {
		c.HttpClient = DefaultConfig().HttpClient
	}

	var clientCert tls.Certificate
	foundClientCert := false
	if t.CACert != "" || t.CAPath != "" || t.ClientCert != "" || t.ClientKey != "" || t.Insecure {
		if t.ClientCert != "" && t.ClientKey != "" {
			var err error
			clientCert, err = tls.LoadX509KeyPair(t.ClientCert, t.ClientKey)
			if err != nil {
				return err
			}
			foundClientCert = true
		} else if t.ClientCert != "" || t.ClientKey != "" {
			return fmt.Errorf("Both client cert and client key must be provided")
		}
	}

	clientTLSConfig := c.HttpClient.Transport.(*http.Transport).TLSClientConfig
	rootConfig := &rootcerts.Config{
		CAFile: t.CACert,
		CAPath: t.CAPath,
	}
	if err := rootcerts.ConfigureTLS(clientTLSConfig, rootConfig); err != nil {
		return err
	}

	clientTLSConfig.InsecureSkipVerify = t.Insecure

	if foundClientCert {
		clientTLSConfig.Certificates = []tls.Certificate{clientCert}
	}
	if t.TLSServerName != "" {
		clientTLSConfig.ServerName = t.TLSServerName
	}

	return nil
}

// ReadEnvironment reads configuration information from the
// environment. If there is an error, no configuration value
// is updated.
func (c *Config) ReadEnvironment() error {
	var envAddress string
	var envCACert string
	var envCAPath string
	var envClientCert string
	var envClientKey string
	var envInsecure bool
	var envTLSServerName string
	var envMaxRetries *uint64

	// Parse the environment variables
	if v := os.Getenv(EnvVaultAddress); v != "" {
		envAddress = v
	}
	if v := os.Getenv(EnvVaultMaxRetries); v != "" {
		maxRetries, err := strconv.ParseUint(v, 10, 32)
		if err != nil {
			return err
		}
		envMaxRetries = &maxRetries
	}
	if v := os.Getenv(EnvVaultCACert); v != "" {
		envCACert = v
	}
	if v := os.Getenv(EnvVaultCAPath); v != "" {
		envCAPath = v
	}
	if v := os.Getenv(EnvVaultClientCert); v != "" {
		envClientCert = v
	}
	if v := os.Getenv(EnvVaultClientKey); v != "" {
		envClientKey = v
	}
	if v := os.Getenv(EnvVaultInsecure); v != "" {
		var err error
		envInsecure, err = strconv.ParseBool(v)
		if err != nil {
			return fmt.Errorf("Could not parse VAULT_SKIP_VERIFY")
		}
	}
	if v := os.Getenv(EnvVaultTLSServerName); v != "" {
		envTLSServerName = v
	}

	// Configure the HTTP clients TLS configuration.
	t := &TLSConfig{
		CACert:        envCACert,
		CAPath:        envCAPath,
		ClientCert:    envClientCert,
		ClientKey:     envClientKey,
		TLSServerName: envTLSServerName,
		Insecure:      envInsecure,
	}
	if err := c.ConfigureTLS(t); err != nil {
		return err
	}

	if envAddress != "" {
		c.Address = envAddress
	}

	if envMaxRetries != nil {
		c.MaxRetries = int(*envMaxRetries) + 1
	}

	return nil
}

// Client is the client to the Vault API. Create a client with
// NewClient.
type Client struct {
	addr               *url.URL
	config             *Config
	token              string
	wrappingLookupFunc WrappingLookupFunc
}

// NewClient returns a new client for the given configuration.
//
// If the environment variable `VAULT_TOKEN` is present, the token will be
// automatically added to the client. Otherwise, you must manually call
// `SetToken()`.
func NewClient(c *Config) (*Client, error) {
	if c == nil {
		c = DefaultConfig()
		if err := c.ReadEnvironment(); err != nil {
			return nil, fmt.Errorf("error reading environment: %v", err)
		}
	}

	u, err := url.Parse(c.Address)
	if err != nil {
		return nil, err
	}

	if c.HttpClient == nil {
		c.HttpClient = DefaultConfig().HttpClient
	}

	tp := c.HttpClient.Transport.(*http.Transport)
	if err := http2.ConfigureTransport(tp); err != nil {
		return nil, err
	}

	redirFunc := func() {
		// Ensure redirects are not automatically followed
		// Note that this is sane for the API client as it has its own
		// redirect handling logic (and thus also for command/meta),
		// but in e.g. http_test actual redirect handling is necessary
		c.HttpClient.CheckRedirect = func(req *http.Request, via []*http.Request) error {
			// Returning this value causes the Go net library to not close the
			// response body and to nil out the error. Otherwise pester tries
			// three times on every redirect because it sees an error from this
			// function (to prevent redirects) passing through to it.
			return http.ErrUseLastResponse
		}
	}

	c.redirectSetup.Do(redirFunc)

	client := &Client{
		addr:   u,
		config: c,
	}

	if token := os.Getenv(EnvVaultToken); token != "" {
		client.SetToken(token)
	}

	return client, nil
}

// Sets the address of Vault in the client. The format of address should be
// "<Scheme>://<Host>:<Port>". Setting this on a client will override the
// value of VAULT_ADDR environment variable.
func (c *Client) SetAddress(addr string) error {
	var err error
	if c.addr, err = url.Parse(addr); err != nil {
		return fmt.Errorf("failed to set address: %v", err)
	}

	return nil
}

// Address returns the Vault URL the client is configured to connect to
func (c *Client) Address() string {
	return c.addr.String()
}

// SetMaxRetries sets the number of retries that will be used in the case of certain errors
func (c *Client) SetMaxRetries(retries int) {
	c.config.MaxRetries = retries
}

// SetWrappingLookupFunc sets a lookup function that returns desired wrap TTLs
// for a given operation and path
func (c *Client) SetWrappingLookupFunc(lookupFunc WrappingLookupFunc) {
	c.wrappingLookupFunc = lookupFunc
}

// Token returns the access token being used by this client. It will
// return the empty string if there is no token set.
func (c *Client) Token() string {
	return c.token
}

// SetToken sets the token directly. This won't perform any auth
// verification, it simply sets the token properly for future requests.
func (c *Client) SetToken(v string) {
	c.token = v
}

// ClearToken deletes the token if it is set or does nothing otherwise.
func (c *Client) ClearToken() {
	c.token = ""
}

// NewRequest creates a new raw request object to query the Vault server
// configured for this client. This is an advanced method and generally
// doesn't need to be called externally.
func (c *Client) NewRequest(method, path string) *Request {
	req := &Request{
		Method: method,
		URL: &url.URL{
			Scheme: c.addr.Scheme,
			Host:   c.addr.Host,
			Path:   path,
		},
		ClientToken: c.token,
		Params:      make(map[string][]string),
	}

	var lookupPath string
	switch {
	case strings.HasPrefix(path, "/v1/"):
		lookupPath = strings.TrimPrefix(path, "/v1/")
	case strings.HasPrefix(path, "v1/"):
		lookupPath = strings.TrimPrefix(path, "v1/")
	default:
		lookupPath = path
	}
	if c.wrappingLookupFunc != nil {
		req.WrapTTL = c.wrappingLookupFunc(method, lookupPath)
	} else {
		req.WrapTTL = DefaultWrappingLookupFunc(method, lookupPath)
	}

	return req
}

// RawRequest performs the raw request given. This request may be against
// a Vault server not configured with this client. This is an advanced operation
// that generally won't need to be called externally.
func (c *Client) RawRequest(r *Request) (*Response, error) {
	redirectCount := 0
START:
	req, err := r.ToHTTP()
	if err != nil {
		return nil, err
	}

	client := pester.NewExtendedClient(c.config.HttpClient)
	client.Backoff = pester.LinearJitterBackoff
	client.MaxRetries = c.config.MaxRetries

	var result *Response
	resp, err := client.Do(req)
	if resp != nil {
		result = &Response{Response: resp}
	}
	if err != nil {
		if strings.Contains(err.Error(), "tls: oversized") {
			err = fmt.Errorf(
				"%s\n\n"+
					"This error usually means that the server is running with TLS disabled\n"+
					"but the client is configured to use TLS. Please either enable TLS\n"+
					"on the server or run the client with -address set to an address\n"+
					"that uses the http protocol:\n\n"+
					"    vault <command> -address http://<address>\n\n"+
					"You can also set the VAULT_ADDR environment variable:\n\n\n"+
					"    VAULT_ADDR=http://<address> vault <command>\n\n"+
					"where <address> is replaced by the actual address to the server.",
				err)
		}
		return result, err
	}

	// Check for a redirect, only allowing for a single redirect
	if (resp.StatusCode == 301 || resp.StatusCode == 302 || resp.StatusCode == 307) && redirectCount == 0 {
		// Parse the updated location
		respLoc, err := resp.Location()
		if err != nil {
			return result, err
		}

		// Ensure a protocol downgrade doesn't happen
		if req.URL.Scheme == "https" && respLoc.Scheme != "https" {
			return result, fmt.Errorf("redirect would cause protocol downgrade")
		}

		// Update the request
		r.URL = respLoc

		// Reset the request body if any
		if err := r.ResetJSONBody(); err != nil {
			return result, err
		}

		// Retry the request
		redirectCount++
		goto START
	}

	if err := result.Error(); err != nil {
		return result, err
	}

	return result, nil
}
<|endoftext|>


<commit_before>package exhibit

import (
	"fmt"
	"regexp"
	"runtime"
)

const maxdepth int = 12

var testname *regexp.Regexp = regexp.MustCompile(`^.*\.Test[^a-z].*`)

type callerInfo struct {
	file, function string
}

func getCallerInfo() (*callerInfo, error) {

	for i := 2; i < maxdepth; i++ {

		pc, file, _, ok := runtime.Caller(i)
		if !ok {
			return nil, fmt.Errorf("Could not retrieve caller %d", i)
		}

		caller := runtime.FuncForPC(pc)

		if testname.MatchString(caller.Name()) {
			return &callerInfo{
				file:     file,
				function: caller.Name(),
			}, nil
		}
	}

	return nil, fmt.Errorf("Max stack depth (%d) reached, no test method found", maxdepth)
}
<commit_msg>Removed max stack depth<commit_after>package exhibit

import (
	"fmt"
	"regexp"
	"runtime"
)

var testname *regexp.Regexp = regexp.MustCompile(`^.*\.Test[^a-z].*`)

type callerInfo struct {
	file, function string
}

func getCallerInfo() (*callerInfo, error) {

	for i := 2; ; i++ {

		pc, file, _, ok := runtime.Caller(i)
		if !ok {
			return nil, fmt.Errorf("Could not retrieve caller %d", i)
		}

		caller := runtime.FuncForPC(pc)

		if testname.MatchString(caller.Name()) {
			return &callerInfo{
				file:     file,
				function: caller.Name(),
			}, nil
		}
	}
}
<|endoftext|>