<commit_before>package geojson

import (
	"bytes"
	"strconv"
	"math"

	"github.com/tidwall/tile38/geojson/poly"
)

// BBox is a bounding box
type BBox struct {
	Min Position
	Max Position
}

// New2DBBox creates a new bounding box
func New2DBBox(minX, minY, maxX, maxY float64) BBox {
	return BBox{Min: Position{X: minX, Y: minY, Z: 0}, Max: Position{X: maxX, Y: maxY, Z: 0}}
}

func fillBBox(m map[string]interface{}) (*BBox, error) {
	var bbox *BBox
	var ok bool
	switch v := m["bbox"].(type) {
	default:
		return nil, errBBoxInvalidType
	case nil:
	case []interface{}:
		if !(len(v) == 4 || len(v) == 6) {
			return nil, errBBoxInvalidNumberOfValues
		}
		bbox = &BBox{}
		if bbox.Min.X, ok = v[0].(float64); !ok {
			return nil, errBBoxInvalidValue
		}
		if bbox.Min.Y, ok = v[1].(float64); !ok {
			return nil, errBBoxInvalidValue
		}
		i := 2
		if len(v) == 6 {
			if bbox.Min.Z, ok = v[2].(float64); !ok {
				return nil, errBBoxInvalidValue
			}
			i = 3
		} else {
			bbox.Min.Z = nilz
		}
		if bbox.Max.X, ok = v[i+0].(float64); !ok {
			return nil, errBBoxInvalidValue
		}
		if bbox.Max.Y, ok = v[i+1].(float64); !ok {
			return nil, errBBoxInvalidValue
		}
		if len(v) == 6 {
			if bbox.Max.Z, ok = v[i+2].(float64); !ok {
				return nil, errBBoxInvalidValue
			}
		} else {
			bbox.Max.Z = nilz
		}
	}
	return bbox, nil
}

func (b *BBox) isCordZDefined() bool {
	return b != nil && (b.Min.Z != nilz || b.Max.Z != nilz)
}

func (b *BBox) write(buf *bytes.Buffer) {
	if b == nil {
		return
	}
	hasZ := b.Min.Z != nilz && b.Max.Z != nilz
	buf.WriteString(`,"bbox":[`)
	buf.WriteString(strconv.FormatFloat(b.Min.X, 'f', -1, 64))
	buf.WriteByte(',')
	buf.WriteString(strconv.FormatFloat(b.Min.Y, 'f', -1, 64))
	if hasZ {
		buf.WriteByte(',')
		buf.WriteString(strconv.FormatFloat(b.Min.Z, 'f', -1, 64))
	}
	buf.WriteByte(',')
	buf.WriteString(strconv.FormatFloat(b.Max.X, 'f', -1, 64))
	buf.WriteByte(',')
	buf.WriteString(strconv.FormatFloat(b.Max.Y, 'f', -1, 64))
	if hasZ {
		buf.WriteByte(',')
		buf.WriteString(strconv.FormatFloat(b.Max.Z, 'f', -1, 64))
	}
	buf.WriteByte(']')
}

func (b BBox) center() Position {
	return Position{
		(b.Max.X-b.Min.X)/2 + b.Min.X,
		(b.Max.Y-b.Min.Y)/2 + b.Min.Y,
		0,
	}
}

func (b BBox) union(bbox BBox) BBox {
	if bbox.Min.X < b.Min.X {
		b.Min.X = bbox.Min.X
	}
	if bbox.Min.Y < b.Min.Y {
		b.Min.Y = bbox.Min.Y
	}
	if bbox.Max.X > b.Max.X {
		b.Max.X = bbox.Max.X
	}
	if bbox.Max.Y > b.Max.Y {
		b.Max.Y = bbox.Max.Y
	}
	return b
}

func (b BBox) exterior() []Position {
	return []Position{
		{b.Min.X, b.Min.Y, 0},
		{b.Min.X, b.Max.Y, 0},
		{b.Max.X, b.Max.Y, 0},
		{b.Max.X, b.Min.Y, 0},
		{b.Min.X, b.Min.Y, 0},
	}
}

func rectBBox(bbox BBox) poly.Rect {
	return poly.Rect{
		Min: poly.Point{X: bbox.Min.X, Y: bbox.Min.Y, Z: 0},
		Max: poly.Point{X: bbox.Max.X, Y: bbox.Max.Y, Z: 0},
	}
}

// ExternalJSON is the simple json representation of the bounding box used for external applications.
func (b BBox) ExternalJSON() string {
	sw, ne := b.Min, b.Max
	sw.Z, ne.Z = 0, 0
	return `{"sw":` + sw.ExternalJSON() + `,"ne":` + ne.ExternalJSON() + `}`
}

// Sparse returns back an evenly distributed number of sub bboxs.
func (b BBox) Sparse(amount byte) []BBox {
	if amount == 0 {
		return []BBox{b}
	}
	var bboxes []BBox
	split := 1 << amount
	var xsize, ysize float64
	if b.Max.X < b.Min.X {
		// crosses the prime meridian
		xsize = (b.Min.X - b.Max.X) / float64(split)
	} else {
		xsize = (b.Max.X - b.Min.X) / float64(split)
	}
	if b.Max.Y < b.Min.Y {
		// crosses the equator
		ysize = (b.Min.Y - b.Max.Y) / float64(split)
	} else {
		ysize = (b.Max.Y - b.Min.Y) / float64(split)
	}

	for y := b.Min.Y; y < b.Max.Y; y += ysize {
		for x := b.Min.X; x < b.Max.X; x += xsize {
			bboxes = append(bboxes, BBox{
				Min: Position{X: x, Y: y, Z: b.Min.Z},
				Max: Position{X: x + xsize, Y: y + ysize, Z: b.Max.Z},
			})
		}
	}
	return bboxes
}

// BBoxesFromCenter calculates the bounding box surrounding a circle.
func BBoxesFromCenter(lat, lon, meters float64) (outer BBox) {

	outer.Min.Y, outer.Min.X, outer.Max.Y, outer.Max.X = BBoxBounds(lat, lon, meters)
	if outer.Min.X == outer.Max.X {
		switch outer.Min.X {
		case -180:
			outer.Max.X = 180
		case 180:
			outer.Min.X = -180
		}
	}

	return outer
}

func BBoxBounds(lat, lon, meters float64) (latMin, lonMin, latMax, lonMax float64) {

	// see http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates#Latitude
	lat = toRadians(lat)
	lon = toRadians(lon)

	r := meters / earthRadius // angular radius

	latMin = lat - r
	latMax = lat + r

	latT := math.Asin(math.Sin(lat) / math.Cos(r))
	lonΔ := math.Acos(( math.Cos(r) - math.Sin(latT) * math.Sin(lat)) / (math.Cos(latT) * math.Cos(lat) ))

	lonMin = lon - lonΔ
	lonMax = lon + lonΔ

	// Adjust for north poll
	if latMax > math.Pi/2 {
		lonMin = -math.Pi
		latMax = math.Pi/2
		lonMax = math.Pi
	}

	// Adjust for south poll
	if latMin < -math.Pi/2 {
		latMin = -math.Pi/2
		lonMin = -math.Pi
		lonMax = math.Pi
	}

	// Adjust for wraparound. Remove this if the commented-out condition below this block is added.
	if lonMin < -math.Pi || lonMax > math.Pi {
		lonMin = -math.Pi
		lonMax = math.Pi
	}

/*
	// Consider splitting area into two bboxes, using the below checks, and erasing above block for performance. See http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates#PolesAnd180thMeridian

	// Adjust for wraparound if minimum longitude is less than -180 degrees.
	if lonMin < -math.Pi {
// box 1:
		latMin = latMin
		latMax = latMax
		lonMin += 2*math.Pi
		lonMax = math.Pi
// box 2:
		latMin = latMin
		latMax = latMax
		lonMin = -math.Pi
		lonMax = lonMax
	}

	// Adjust for wraparound if minimum longitude is greater than 180 degrees.
	if lonMax > math.Pi {
// box 1:
		latMin = latMin
		latMax = latMax
		lonMin = lonMin
		lonMax = -math.Pi
// box 2:
		latMin = latMin
		latMax = latMax
		lonMin = -math.Pi
		lonMax -= 2*math.Pi
	}
*/

	lonMin = math.Mod(lonMin+3*math.Pi, 2*math.Pi) - math.Pi // normalise to -180..+180°
	lonMax = math.Mod(lonMax+3*math.Pi, 2*math.Pi) - math.Pi

	return toDegrees(latMin), toDegrees(lonMin), toDegrees(latMax), toDegrees(lonMax)
}
<commit_msg>Typo on line 244: minimum -> maximum<commit_after>package geojson

import (
	"bytes"
	"strconv"
	"math"

	"github.com/tidwall/tile38/geojson/poly"
)

// BBox is a bounding box
type BBox struct {
	Min Position
	Max Position
}

// New2DBBox creates a new bounding box
func New2DBBox(minX, minY, maxX, maxY float64) BBox {
	return BBox{Min: Position{X: minX, Y: minY, Z: 0}, Max: Position{X: maxX, Y: maxY, Z: 0}}
}

func fillBBox(m map[string]interface{}) (*BBox, error) {
	var bbox *BBox
	var ok bool
	switch v := m["bbox"].(type) {
	default:
		return nil, errBBoxInvalidType
	case nil:
	case []interface{}:
		if !(len(v) == 4 || len(v) == 6) {
			return nil, errBBoxInvalidNumberOfValues
		}
		bbox = &BBox{}
		if bbox.Min.X, ok = v[0].(float64); !ok {
			return nil, errBBoxInvalidValue
		}
		if bbox.Min.Y, ok = v[1].(float64); !ok {
			return nil, errBBoxInvalidValue
		}
		i := 2
		if len(v) == 6 {
			if bbox.Min.Z, ok = v[2].(float64); !ok {
				return nil, errBBoxInvalidValue
			}
			i = 3
		} else {
			bbox.Min.Z = nilz
		}
		if bbox.Max.X, ok = v[i+0].(float64); !ok {
			return nil, errBBoxInvalidValue
		}
		if bbox.Max.Y, ok = v[i+1].(float64); !ok {
			return nil, errBBoxInvalidValue
		}
		if len(v) == 6 {
			if bbox.Max.Z, ok = v[i+2].(float64); !ok {
				return nil, errBBoxInvalidValue
			}
		} else {
			bbox.Max.Z = nilz
		}
	}
	return bbox, nil
}

func (b *BBox) isCordZDefined() bool {
	return b != nil && (b.Min.Z != nilz || b.Max.Z != nilz)
}

func (b *BBox) write(buf *bytes.Buffer) {
	if b == nil {
		return
	}
	hasZ := b.Min.Z != nilz && b.Max.Z != nilz
	buf.WriteString(`,"bbox":[`)
	buf.WriteString(strconv.FormatFloat(b.Min.X, 'f', -1, 64))
	buf.WriteByte(',')
	buf.WriteString(strconv.FormatFloat(b.Min.Y, 'f', -1, 64))
	if hasZ {
		buf.WriteByte(',')
		buf.WriteString(strconv.FormatFloat(b.Min.Z, 'f', -1, 64))
	}
	buf.WriteByte(',')
	buf.WriteString(strconv.FormatFloat(b.Max.X, 'f', -1, 64))
	buf.WriteByte(',')
	buf.WriteString(strconv.FormatFloat(b.Max.Y, 'f', -1, 64))
	if hasZ {
		buf.WriteByte(',')
		buf.WriteString(strconv.FormatFloat(b.Max.Z, 'f', -1, 64))
	}
	buf.WriteByte(']')
}

func (b BBox) center() Position {
	return Position{
		(b.Max.X-b.Min.X)/2 + b.Min.X,
		(b.Max.Y-b.Min.Y)/2 + b.Min.Y,
		0,
	}
}

func (b BBox) union(bbox BBox) BBox {
	if bbox.Min.X < b.Min.X {
		b.Min.X = bbox.Min.X
	}
	if bbox.Min.Y < b.Min.Y {
		b.Min.Y = bbox.Min.Y
	}
	if bbox.Max.X > b.Max.X {
		b.Max.X = bbox.Max.X
	}
	if bbox.Max.Y > b.Max.Y {
		b.Max.Y = bbox.Max.Y
	}
	return b
}

func (b BBox) exterior() []Position {
	return []Position{
		{b.Min.X, b.Min.Y, 0},
		{b.Min.X, b.Max.Y, 0},
		{b.Max.X, b.Max.Y, 0},
		{b.Max.X, b.Min.Y, 0},
		{b.Min.X, b.Min.Y, 0},
	}
}

func rectBBox(bbox BBox) poly.Rect {
	return poly.Rect{
		Min: poly.Point{X: bbox.Min.X, Y: bbox.Min.Y, Z: 0},
		Max: poly.Point{X: bbox.Max.X, Y: bbox.Max.Y, Z: 0},
	}
}

// ExternalJSON is the simple json representation of the bounding box used for external applications.
func (b BBox) ExternalJSON() string {
	sw, ne := b.Min, b.Max
	sw.Z, ne.Z = 0, 0
	return `{"sw":` + sw.ExternalJSON() + `,"ne":` + ne.ExternalJSON() + `}`
}

// Sparse returns back an evenly distributed number of sub bboxs.
func (b BBox) Sparse(amount byte) []BBox {
	if amount == 0 {
		return []BBox{b}
	}
	var bboxes []BBox
	split := 1 << amount
	var xsize, ysize float64
	if b.Max.X < b.Min.X {
		// crosses the prime meridian
		xsize = (b.Min.X - b.Max.X) / float64(split)
	} else {
		xsize = (b.Max.X - b.Min.X) / float64(split)
	}
	if b.Max.Y < b.Min.Y {
		// crosses the equator
		ysize = (b.Min.Y - b.Max.Y) / float64(split)
	} else {
		ysize = (b.Max.Y - b.Min.Y) / float64(split)
	}

	for y := b.Min.Y; y < b.Max.Y; y += ysize {
		for x := b.Min.X; x < b.Max.X; x += xsize {
			bboxes = append(bboxes, BBox{
				Min: Position{X: x, Y: y, Z: b.Min.Z},
				Max: Position{X: x + xsize, Y: y + ysize, Z: b.Max.Z},
			})
		}
	}
	return bboxes
}

// BBoxesFromCenter calculates the bounding box surrounding a circle.
func BBoxesFromCenter(lat, lon, meters float64) (outer BBox) {

	outer.Min.Y, outer.Min.X, outer.Max.Y, outer.Max.X = BBoxBounds(lat, lon, meters)
	if outer.Min.X == outer.Max.X {
		switch outer.Min.X {
		case -180:
			outer.Max.X = 180
		case 180:
			outer.Min.X = -180
		}
	}

	return outer
}

func BBoxBounds(lat, lon, meters float64) (latMin, lonMin, latMax, lonMax float64) {

	// see http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates#Latitude
	lat = toRadians(lat)
	lon = toRadians(lon)

	r := meters / earthRadius // angular radius

	latMin = lat - r
	latMax = lat + r

	latT := math.Asin(math.Sin(lat) / math.Cos(r))
	lonΔ := math.Acos(( math.Cos(r) - math.Sin(latT) * math.Sin(lat)) / (math.Cos(latT) * math.Cos(lat) ))

	lonMin = lon - lonΔ
	lonMax = lon + lonΔ

	// Adjust for north poll
	if latMax > math.Pi/2 {
		lonMin = -math.Pi
		latMax = math.Pi/2
		lonMax = math.Pi
	}

	// Adjust for south poll
	if latMin < -math.Pi/2 {
		latMin = -math.Pi/2
		lonMin = -math.Pi
		lonMax = math.Pi
	}

	// Adjust for wraparound. Remove this if the commented-out condition below this block is added.
	if lonMin < -math.Pi || lonMax > math.Pi {
		lonMin = -math.Pi
		lonMax = math.Pi
	}

/*
	// Consider splitting area into two bboxes, using the below checks, and erasing above block for performance. See http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates#PolesAnd180thMeridian

	// Adjust for wraparound if minimum longitude is less than -180 degrees.
	if lonMin < -math.Pi {
// box 1:
		latMin = latMin
		latMax = latMax
		lonMin += 2*math.Pi
		lonMax = math.Pi
// box 2:
		latMin = latMin
		latMax = latMax
		lonMin = -math.Pi
		lonMax = lonMax
	}

	// Adjust for wraparound if maximum longitude is greater than 180 degrees.
	if lonMax > math.Pi {
// box 1:
		latMin = latMin
		latMax = latMax
		lonMin = lonMin
		lonMax = -math.Pi
// box 2:
		latMin = latMin
		latMax = latMax
		lonMin = -math.Pi
		lonMax -= 2*math.Pi
	}
*/

	lonMin = math.Mod(lonMin+3*math.Pi, 2*math.Pi) - math.Pi // normalise to -180..+180°
	lonMax = math.Mod(lonMax+3*math.Pi, 2*math.Pi) - math.Pi

	return toDegrees(latMin), toDegrees(lonMin), toDegrees(latMax), toDegrees(lonMax)
}
<|endoftext|>


<commit_before>package geom

// Drawing of 2-dimensional primitives.

import (
	"image"
	"image/color"
	"image/draw"
	"math"
)

// The Canvas interface encapsulates the functions used to draw
// geometric primitives.  The canvas should be oriented such that
// the lower left corner is the point 0,0, up is positive Y and right is
// positive X.
type Canvas interface {
	Size() (int, int)
	StrokeLine(c color.Color, x0, y0, x1, y1 int)
	FillCircle(c color.Color, x, y, r int)
}

// Draw draws a point on the canvas.
func (pt Point) Draw(cv Canvas, cl color.Color) {
	const radius = 4
	x0, y0 := int(pt[0]+0.5), int(pt[1]+0.5)
	cv.FillCircle(cl, x0, y0, radius)
}

// DrawAt draws the vector extending from a given point.
func (v Vector) DrawAt(cv Canvas, cl color.Color, p Point) {
	p.Draw(cv, cl)
	x0, y0 := int(p[0]+0.5), int(p[1]+0.5)
	p1 := p.Plus(v)
	x1, y1 := int(p1[0]+0.5), int(p1[1]+0.5)
	cv.StrokeLine(cl, x0, y0, x1, y1)
}

// Draw draws a ray on the canvas.
func (ray Ray) Draw(cv Canvas, cl color.Color) {
	const length = 25
	ray.Direction.ScaledBy(length).DrawAt(cv, cl, ray.Origin)
}

// Draw draws a line on the canvas.
func (l Line) Draw(cv Canvas, cl color.Color) {
	wi, hi := cv.Size()
	w, h := float64(wi), float64(hi)
	segs := [4]Line{
		{Origin: Point{0, 0}, Normal: Vector{0, 1}},
		{Origin: Point{0, 0}, Normal: Vector{1, 0}},
		{Origin: Point{w - 1, h - 1}, Normal: Vector{0, -1}},
		{Origin: Point{w - 1, h - 1}, Normal: Vector{-1, 0}},
	}

	var ends []Point
	for _, s := range segs {
		p, hit := l.LineIntersection(s)
		if hit && onCanvas(p, cv) && (len(ends) == 0 || !p.Equals(ends[0])) {
			ends = append(ends, p)
		}
	}

	x0, y0 := int(ends[0][0]+0.5), int(ends[0][1]+0.5)
	x1, y1 := int(ends[1][0]+0.5), int(ends[1][1]+0.5)
	cv.StrokeLine(cl, x0, y0, x1, y1)

	len := ends[0].Distance(ends[1])
	dir := l.Direction()
	p := ends[0].Plus(dir.ScaledBy(len / 2))
	if p[0] < 0 || p[0] >= w || p[1] < 0 || p[1] >= h {
		p = ends[1].Plus(dir.ScaledBy(len / 2))
	}
	Ray{Origin: p, Direction: l.Normal}.Draw(cv, cl)
}

func onCanvas(p Point, cv Canvas) bool {
	wi, hi := cv.Size()
	w, h := float64(wi), float64(hi)
	return p[0] >= 0 && p[0] < w && p[1] >= 0 && p[1] < h
}

// Draw draws the segment on the canvas.
func (s Segment) Draw(cv Canvas, cl color.Color) {
	const length = 25
	s[0].Draw(cv, cl)
	s[1].Draw(cv, cl)
	x0, y0 := int(s[0][0]+0.5), int(s[0][1]+0.5)
	x1, y1 := int(s[1][0]+0.5), int(s[1][1]+0.5)
	cv.StrokeLine(cl, x0, y0, x1, y1)
	s.Normal().ScaledBy(length).DrawAt(cv, cl, s.Center())
}

// Draw draws a circle on the canvas.
func (cir Circle) Draw(cv Canvas, cl color.Color) {
	const N = 100
	const dt = 2 * math.Pi / N

	x0 := int(cir.Center[0] + cir.Radius + 0.5)
	y0 := int(cir.Center[1] + 0.5)
	for i := 1; i < N+1; i++ {
		t := float64(i) * dt
		x1 := int(cir.Center[0] + cir.Radius*math.Cos(t) + 0.5)
		y1 := int(cir.Center[1] + cir.Radius*math.Sin(t) + 0.5)
		cv.StrokeLine(cl, x0, y0, x1, y1)
		x0, y0 = x1, y1
	}
}

// An ImageCanvas implements the Canvas interface using the
// image/draw package from the Go standard library.
type ImageCanvas struct {
	draw.Image
}

// Size returns the size of the canvas in pixels.
func (img ImageCanvas) Size() (int, int) {
	b := img.Bounds()
	return b.Max.X - b.Min.X, b.Max.Y - b.Min.Y
}

// ToImgCoords returns x and y transformed from the Canvas
// frame (0,0 in the lower left, etc.) to the image frame.
func (img ImageCanvas) toImgCoords(x, y int) (int, int) {
	b := img.Bounds()
	h := b.Max.Y - b.Min.Y
	x = x + b.Min.X
	y = h - y - 1 + b.Min.Y
	return x, y
}

// StrokeLine draws a colored line on the canvas.
func (img ImageCanvas) StrokeLine(c color.Color, x0, y0, x1, y1 int) {
	x0, y0 = img.toImgCoords(x0, y0)
	x1, y1 = img.toImgCoords(x1, y1)

	// Bresenham's alg: http://en.wikipedia.org/wiki/Bresenham's_line_algorithm
	steep := abs(y0-y1) > abs(x0-x1)
	if steep {
		x0, y0 = y0, x0
		x1, y1 = y1, x1
	}
	if x0 > x1 {
		x0, x1 = x1, x0
		y0, y1 = y1, y0
	}
	dx := x1 - x0
	dy := abs(y1 - y0)
	err := dx / 2
	y := y0

	ystep := -1
	if y0 < y1 {
		ystep = 1
	}

	for x := x0; x <= x1; x++ {
		if steep {
			img.Set(y, x, c)
		} else {
			img.Set(x, y, c)
		}
		err -= dy
		if err < 0 {
			y += ystep
			err += dx
		}
	}
}

func abs(i int) int {
	if i < 0 {
		return -i
	}
	return i
}

// FillCircle fills a colored circle on the canvas.
func (img ImageCanvas) FillCircle(c color.Color, x, y, r int) {
	p := image.Pt(img.toImgCoords(x, y))
	src := image.NewUniform(c)
	draw.DrawMask(img, img.Bounds(), src, image.ZP, &dot{p, r}, image.ZP, draw.Over)
}

type dot struct {
	p image.Point
	r int
}

func (c dot) ColorModel() color.Model {
	return color.AlphaModel
}

func (c dot) Bounds() image.Rectangle {
	return image.Rect(c.p.X-c.r, c.p.Y-c.r, c.p.X+c.r, c.p.Y+c.r)
}

func (c dot) At(x, y int) color.Color {
	xx, yy, rr := float64(x-c.p.X)+0.5, float64(y-c.p.Y)+0.5, float64(c.r)
	if xx*xx+yy*yy < rr*rr {
		return color.Alpha{255}
	}
	return color.Alpha{0}
}
<commit_msg>Add a function to round float64s to ints.<commit_after>package geom

// Drawing of 2-dimensional primitives.

import (
	"image"
	"image/color"
	"image/draw"
	"math"
)

// The Canvas interface encapsulates the functions used to draw
// geometric primitives.  The canvas should be oriented such that
// the lower left corner is the point 0,0, up is positive Y and right is
// positive X.
type Canvas interface {
	Size() (int, int)
	StrokeLine(c color.Color, x0, y0, x1, y1 int)
	FillCircle(c color.Color, x, y, r int)
}

// Draw draws a point on the canvas.
func (pt Point) Draw(cv Canvas, cl color.Color) {
	const radius = 4
	x0, y0 := round(pt[0]), round(pt[1])
	cv.FillCircle(cl, x0, y0, radius)
}

// DrawAt draws the vector extending from a given point.
func (v Vector) DrawAt(cv Canvas, cl color.Color, p Point) {
	p.Draw(cv, cl)
	x0, y0 := round(p[0]), round(p[1])
	p1 := p.Plus(v)
	x1, y1 := round(p1[0]), round(p1[1])
	cv.StrokeLine(cl, x0, y0, x1, y1)
}

// Draw draws a ray on the canvas.
func (ray Ray) Draw(cv Canvas, cl color.Color) {
	const length = 25
	ray.Direction.ScaledBy(length).DrawAt(cv, cl, ray.Origin)
}

// Draw draws a line on the canvas.
func (l Line) Draw(cv Canvas, cl color.Color) {
	wi, hi := cv.Size()
	w, h := float64(wi), float64(hi)
	segs := [4]Line{
		{Origin: Point{0, 0}, Normal: Vector{0, 1}},
		{Origin: Point{0, 0}, Normal: Vector{1, 0}},
		{Origin: Point{w - 1, h - 1}, Normal: Vector{0, -1}},
		{Origin: Point{w - 1, h - 1}, Normal: Vector{-1, 0}},
	}

	var ends []Point
	for _, s := range segs {
		p, hit := l.LineIntersection(s)
		if hit && onCanvas(p, cv) && (len(ends) == 0 || !p.Equals(ends[0])) {
			ends = append(ends, p)
		}
	}

	x0, y0 := round(ends[0][0]+0.5), int(ends[0][1])
	x1, y1 := round(ends[1][0]+0.5), int(ends[1][1])
	cv.StrokeLine(cl, x0, y0, x1, y1)

	len := ends[0].Distance(ends[1])
	dir := l.Direction()
	p := ends[0].Plus(dir.ScaledBy(len / 2))
	if p[0] < 0 || p[0] >= w || p[1] < 0 || p[1] >= h {
		p = ends[1].Plus(dir.ScaledBy(len / 2))
	}
	Ray{Origin: p, Direction: l.Normal}.Draw(cv, cl)
}

func onCanvas(p Point, cv Canvas) bool {
	wi, hi := cv.Size()
	w, h := float64(wi), float64(hi)
	return p[0] >= 0 && p[0] < w && p[1] >= 0 && p[1] < h
}

// Draw draws the segment on the canvas.
func (s Segment) Draw(cv Canvas, cl color.Color) {
	const length = 25
	s[0].Draw(cv, cl)
	s[1].Draw(cv, cl)
	x0, y0 := round(s[0][0]+0.5), int(s[0][1])
	x1, y1 := round(s[1][0]+0.5), int(s[1][1])
	cv.StrokeLine(cl, x0, y0, x1, y1)
	s.Normal().ScaledBy(length).DrawAt(cv, cl, s.Center())
}

// Draw draws a circle on the canvas.
func (cir Circle) Draw(cv Canvas, cl color.Color) {
	const N = 100
	const dt = 2 * math.Pi / N

	x0 := round(cir.Center[0] + cir.Radius)
	y0 := round(cir.Center[1])
	for i := 1; i < N+1; i++ {
		t := float64(i) * dt
		x1 := round(cir.Center[0] + cir.Radius*math.Cos(t))
		y1 := round(cir.Center[1] + cir.Radius*math.Sin(t))
		cv.StrokeLine(cl, x0, y0, x1, y1)
		x0, y0 = x1, y1
	}
}

func round(f float64) int {
	return int(f + 0.5)
}

// An ImageCanvas implements the Canvas interface using the
// image/draw package from the Go standard library.
type ImageCanvas struct {
	draw.Image
}

// Size returns the size of the canvas in pixels.
func (img ImageCanvas) Size() (int, int) {
	b := img.Bounds()
	return b.Max.X - b.Min.X, b.Max.Y - b.Min.Y
}

// ToImgCoords returns x and y transformed from the Canvas
// frame (0,0 in the lower left, etc.) to the image frame.
func (img ImageCanvas) toImgCoords(x, y int) (int, int) {
	b := img.Bounds()
	h := b.Max.Y - b.Min.Y
	x = x + b.Min.X
	y = h - y - 1 + b.Min.Y
	return x, y
}

// StrokeLine draws a colored line on the canvas.
func (img ImageCanvas) StrokeLine(c color.Color, x0, y0, x1, y1 int) {
	x0, y0 = img.toImgCoords(x0, y0)
	x1, y1 = img.toImgCoords(x1, y1)

	// Bresenham's alg: http://en.wikipedia.org/wiki/Bresenham's_line_algorithm
	steep := abs(y0-y1) > abs(x0-x1)
	if steep {
		x0, y0 = y0, x0
		x1, y1 = y1, x1
	}
	if x0 > x1 {
		x0, x1 = x1, x0
		y0, y1 = y1, y0
	}
	dx := x1 - x0
	dy := abs(y1 - y0)
	err := dx / 2
	y := y0

	ystep := -1
	if y0 < y1 {
		ystep = 1
	}

	for x := x0; x <= x1; x++ {
		if steep {
			img.Set(y, x, c)
		} else {
			img.Set(x, y, c)
		}
		err -= dy
		if err < 0 {
			y += ystep
			err += dx
		}
	}
}

func abs(i int) int {
	if i < 0 {
		return -i
	}
	return i
}

// FillCircle fills a colored circle on the canvas.
func (img ImageCanvas) FillCircle(c color.Color, x, y, r int) {
	p := image.Pt(img.toImgCoords(x, y))
	src := image.NewUniform(c)
	draw.DrawMask(img, img.Bounds(), src, image.ZP, &dot{p, r}, image.ZP, draw.Over)
}

type dot struct {
	p image.Point
	r int
}

func (c dot) ColorModel() color.Model {
	return color.AlphaModel
}

func (c dot) Bounds() image.Rectangle {
	return image.Rect(c.p.X-c.r, c.p.Y-c.r, c.p.X+c.r, c.p.Y+c.r)
}

func (c dot) At(x, y int) color.Color {
	xx, yy, rr := float64(x-c.p.X)+0.5, float64(y-c.p.Y)+0.5, float64(c.r)
	if xx*xx+yy*yy < rr*rr {
		return color.Alpha{255}
	}
	return color.Alpha{0}
}
<|endoftext|>


<commit_before>package admin

import (
	"bytes"
	"errors"
	"fmt"
	"os"
	"path"
	"reflect"
	"strings"
	"text/template"

	"github.com/jinzhu/gorm"
	"github.com/qor/qor"
	"github.com/qor/qor/roles"
)

type Context struct {
	*qor.Context
	*Searcher
	Resource    *Resource
	Admin       *Admin
	CurrentUser qor.CurrentUser
	Result      interface{}
	Content     string
}

// Resource
func (context *Context) ResourcePath() string {
	if context.Resource == nil {
		return ""
	}
	return context.Resource.ToParam()
}

func (context *Context) SetResource(res *Resource) *Context {
	context.Resource = res
	context.Searcher = &Searcher{Context: context}
	return context
}

func (context *Context) NewResource(name ...string) *Context {
	clone := &Context{Context: context.Context, Admin: context.Admin, Result: context.Result}
	if len(name) > 0 {
		clone.SetResource(context.Admin.GetResource(name[0]))
	} else {
		clone.SetResource(context.Resource)
	}
	return clone
}

// Template
func (context *Context) getViewPaths() (paths []string) {
	dirs := []string{context.ResourcePath(), path.Join("themes", "default"), "."}
	if context.Resource != nil && context.Resource.Config != nil && context.Resource.Config.Theme != "" {
		themePath := path.Join("themes", context.Resource.Config.Theme)
		dirs = append([]string{path.Join(themePath, context.ResourcePath()), themePath}, dirs...)
	}

	for _, p := range dirs {
		for _, d := range viewPaths {
			if isExistingDir(path.Join(d, p)) {
				paths = append(paths, path.Join(d, p))
			}
		}
	}
	return paths
}

func (context *Context) findFile(layout string) (string, error) {
	for _, p := range context.getViewPaths() {
		if _, err := os.Stat(path.Join(p, layout)); !os.IsNotExist(err) {
			return path.Join(p, layout), nil
		}
	}
	return "", errors.New("file not found")
}

func (context *Context) findTemplate(tmpl *template.Template, layout string) (*template.Template, error) {
	for _, p := range context.getViewPaths() {
		if _, err := os.Stat(path.Join(p, layout)); !os.IsNotExist(err) {
			if tmpl, err = tmpl.ParseFiles(path.Join(p, layout)); err != nil {
				fmt.Println(err)
			} else {
				return tmpl, nil
			}
		}
	}
	return tmpl, errors.New("template not found")
}

func (context *Context) Render(name string, results ...interface{}) string {
	var err error
	names := strings.Split(name, "/")
	tmpl := template.New(names[len(names)-1] + ".tmpl").Funcs(context.funcMap())
	if len(results) > 0 {
		context.Result = results[0]
	}

	if tmpl, err = context.findTemplate(tmpl, name+".tmpl"); err == nil {
		var result = bytes.NewBufferString("")
		if err := tmpl.Execute(result, context); err != nil {
			fmt.Println(err)
		}
		return result.String()
	}

	return ""
}

func (context *Context) Execute(name string, result interface{}) {
	var tmpl *template.Template
	var cacheKey string

	if context.Resource != nil {
		cacheKey = path.Join(context.ResourcePath(), name)
	} else {
		cacheKey = name
	}

	if t, ok := templates[cacheKey]; !ok || true {
		var err error
		tmpl = template.New("layout.tmpl").Funcs(context.funcMap())
		if tmpl, err = context.findTemplate(tmpl, "layout.tmpl"); err == nil {
			for _, name := range []string{"header", "footer"} {
				if tmpl.Lookup(name) == nil {
					tmpl, _ = context.findTemplate(tmpl, name+".tmpl")
				}
			}
		}
	} else {
		tmpl = t
	}

	context.Content = context.Render(name, result)
	if err := tmpl.Execute(context.Writer, context); err != nil {
		fmt.Println(err)
	}
}

// Function Maps
func (context *Context) PrimaryKeyOf(value interface{}) interface{} {
	return context.GetDB().NewScope(value).PrimaryKeyValue()
}

func (context *Context) NewRecord(value interface{}) interface{} {
	return context.GetDB().NewRecord(value)
}

func (context *Context) ValueOf(value interface{}, meta *Meta) interface{} {
	return meta.Valuer(value, context.Context)
}

func (context *Context) NewResourcePath(value interface{}) string {
	if res, ok := value.(*Resource); ok {
		return path.Join(context.Admin.router.Prefix, res.ToParam(), "new")
	} else {
		return path.Join(context.Admin.router.Prefix, context.Resource.ToParam(), "new")
	}
}

func (context *Context) UrlFor(value interface{}, resources ...*Resource) string {
	var url string
	if admin, ok := value.(*Admin); ok {
		url = admin.router.Prefix
	} else if res, ok := value.(*Resource); ok {
		url = path.Join(context.Admin.router.Prefix, res.ToParam())
	} else {
		structType := reflect.Indirect(reflect.ValueOf(value)).Type().String()
		res := context.Admin.GetResource(structType)
		primaryKey := context.GetDB().NewScope(value).PrimaryKeyValue()
		url = path.Join(context.Admin.router.Prefix, res.ToParam(), fmt.Sprintf("%v", primaryKey))
	}
	return url
}

func (context *Context) LinkTo(text interface{}, link interface{}) string {
	text = reflect.Indirect(reflect.ValueOf(text)).Interface()
	if linkStr, ok := link.(string); ok {
		return fmt.Sprintf(`<a href="%v">%v</a>`, linkStr, text)
	}
	return fmt.Sprintf(`<a href="%v">%v</a>`, context.UrlFor(link), text)
}

func (context *Context) RenderForm(value interface{}, metas []*Meta) string {
	var result = bytes.NewBufferString("")
	context.renderForm(result, value, metas, []string{"QorResource"})
	return result.String()
}

func (context *Context) renderForm(result *bytes.Buffer, value interface{}, metas []*Meta, prefix []string) {
	for _, meta := range metas {
		context.RenderMeta(result, meta, value, prefix)
	}
}

func (context *Context) RenderMeta(writer *bytes.Buffer, meta *Meta, value interface{}, prefix []string) {
	prefix = append(prefix, meta.Name)

	funcsMap := context.funcMap()
	funcsMap["render_form"] = func(value interface{}, metas []*Meta, index ...int) string {
		var result = bytes.NewBufferString("")
		newPrefix := append([]string{}, prefix...)

		if len(index) > 0 {
			last := newPrefix[len(newPrefix)-1]
			newPrefix = append(newPrefix[:len(newPrefix)-1], fmt.Sprintf("%v[%v]", last, index[0]))
		}

		context.renderForm(result, value, metas, newPrefix)
		return result.String()
	}

	var tmpl = template.New(meta.Type + ".tmpl").Funcs(funcsMap)

	if tmpl, err := context.findTemplate(tmpl, fmt.Sprintf("forms/%v.tmpl", meta.Type)); err == nil {
		data := map[string]interface{}{}
		data["InputId"] = strings.Join(prefix, "")
		data["Label"] = meta.Label
		data["InputName"] = strings.Join(prefix, ".")
		value := meta.Valuer(value, context.Context)
		if rv := reflect.ValueOf(value); rv.Kind() == reflect.Ptr && !rv.IsNil() {
			value = rv.Elem().Interface()
		}
		data["Value"] = value
		if meta.GetCollection != nil {
			data["CollectionValue"] = meta.GetCollection(value, context.Context)
		}
		data["Meta"] = meta

		if err := tmpl.Execute(writer, data); err != nil {
			fmt.Println(err)
		}
	} else {
		fmt.Printf("%v: form type %v not supported\n", meta.Name, meta.Type)
	}
}

func (context *Context) HasPrimaryKey(value interface{}, primaryKey interface{}) bool {
	primaryKeys := []interface{}{}
	reflectValue := reflect.ValueOf(value)
	if reflectValue.Kind() == reflect.Ptr {
		reflectValue = reflectValue.Elem()
	}
	if reflectValue.Kind() == reflect.Slice {
		for i := 0; i < reflectValue.Len(); i++ {
			scope := &gorm.Scope{Value: reflectValue.Index(i).Interface()}
			primaryKeys = append(primaryKeys, scope.PrimaryKeyValue())
		}
	} else if reflectValue.Kind() == reflect.Struct {
		scope := &gorm.Scope{Value: value}
		primaryKeys = append(primaryKeys, scope.PrimaryKeyValue())
	}

	for _, key := range primaryKeys {
		if fmt.Sprintf("%v", primaryKey) == fmt.Sprintf("%v", key) {
			return true
		}
	}
	return false
}

func (context *Context) getResource(resources ...*Resource) *Resource {
	for _, res := range resources {
		return res
	}
	return context.Resource
}

func (context *Context) AllMetas(resources ...*Resource) []*Meta {
	return context.getResource(resources...).AllMetas()
}

func (context *Context) IndexMetas(resources ...*Resource) []*Meta {
	res := context.getResource(resources...)
	return res.AllowedMetas(res.IndexMetas(), context, roles.Read)
}

func (context *Context) EditMetas(resources ...*Resource) []*Meta {
	res := context.getResource(resources...)
	return res.AllowedMetas(res.EditMetas(), context, roles.Update)
}

func (context *Context) ShowMetas(resources ...*Resource) []*Meta {
	res := context.getResource(resources...)
	return res.AllowedMetas(res.ShowMetas(), context, roles.Read)
}

func (context *Context) NewMetas(resources ...*Resource) []*Meta {
	res := context.getResource(resources...)
	return res.AllowedMetas(res.NewMetas(), context, roles.Create)
}

func (context *Context) JavaScriptTag(name string) string {
	name = path.Join(context.Admin.GetRouter().Prefix, "assets", "javascripts", name+".js")
	return fmt.Sprintf(`<script src="%s"></script>`, name)
}

func (context *Context) StyleSheetTag(name string) string {
	name = path.Join(context.Admin.GetRouter().Prefix, "assets", "stylesheets", name+".css")
	return fmt.Sprintf(`<link type="text/css" rel="stylesheet" href="%s">`, name)
}

type Page struct {
	Page       int
	Current    bool
	IsPrevious bool
	IsNext     bool
}

func (context *Context) Pagination() []Page {
	pagination := context.Searcher.Pagination
	start := pagination.CurrentPage
	if start-5 < 1 {
		start = 1
	}

	end := start + 9
	if end > pagination.Pages {
		end = pagination.Pages
	}

	var pages []Page
	if start > 1 {
		pages = append(pages, Page{Page: start - 1, IsPrevious: true})
	}

	for i := start; i <= end; i++ {
		pages = append(pages, Page{Page: i, Current: pagination.CurrentPage == i})
	}

	if end < pagination.Pages {
		pages = append(pages, Page{Page: end + 1, IsNext: true})
	}

	return pages
}

func Equal(a, b interface{}) bool {
	return reflect.DeepEqual(a, b)
}

func (context *Context) funcMap() template.FuncMap {
	funcMap := template.FuncMap{
		"menus":             context.Admin.GetMenus,
		"current_user":      func() qor.CurrentUser { return context.CurrentUser },
		"render":            context.Render,
		"render_form":       context.RenderForm,
		"url_for":           context.UrlFor,
		"link_to":           context.LinkTo,
		"new_resource_path": context.NewResourcePath,
		"new_resource":      context.NewResource,
		"is_new_record":     context.NewRecord,
		"value_of":          context.ValueOf,
		"primary_key_of":    context.PrimaryKeyOf,
		"has_primary_key":   context.HasPrimaryKey,
		"all_metas":         context.AllMetas,
		"index_metas":       context.IndexMetas,
		"edit_metas":        context.EditMetas,
		"show_metas":        context.ShowMetas,
		"new_metas":         context.NewMetas,
		"pagination":        context.Pagination,
		"javascript_tag":    context.JavaScriptTag,
		"stylesheet_tag":    context.StyleSheetTag,
		"equal":             Equal,
		"patch_url":         context.PatchURL,
	}

	for key, value := range context.Admin.funcMaps {
		funcMap[key] = value
	}
	return funcMap
}

// PatchURL updates the query part
func (context *Context) PatchURL(parts ...interface{}) (u string, err error) {
	url := *context.Request.URL
	q := url.Query()
	for i := 0; i < len(parts)/2; i++ {
		key, ok := parts[i*2].(string)
		if !ok {
			err = fmt.Errorf("%[1]s type is %[1]T, want string", parts[i*2])
			return
		}
		value, ok := parts[i*2+1].(string)
		if !ok {
			err = fmt.Errorf("%[1]s type is %[1]T, want string", parts[i*2+1])
			return
		}
		if value == "" {
			q.Del(key)
		} else {
			q.Set(key, value)
		}
	}
	url.RawQuery = q.Encode()
	u = url.String()
	return
}
<commit_msg>add help doc<commit_after>package admin

import (
	"bytes"
	"errors"
	"fmt"
	"os"
	"path"
	"reflect"
	"strings"
	"text/template"

	"github.com/jinzhu/gorm"
	"github.com/qor/qor"
	"github.com/qor/qor/roles"
)

type Context struct {
	*qor.Context
	*Searcher
	Resource    *Resource
	Admin       *Admin
	CurrentUser qor.CurrentUser
	Result      interface{}
	Content     string
}

// Resource
func (context *Context) ResourcePath() string {
	if context.Resource == nil {
		return ""
	}
	return context.Resource.ToParam()
}

func (context *Context) SetResource(res *Resource) *Context {
	context.Resource = res
	context.Searcher = &Searcher{Context: context}
	return context
}

func (context *Context) NewResource(name ...string) *Context {
	clone := &Context{Context: context.Context, Admin: context.Admin, Result: context.Result}
	if len(name) > 0 {
		clone.SetResource(context.Admin.GetResource(name[0]))
	} else {
		clone.SetResource(context.Resource)
	}
	return clone
}

// Template
func (context *Context) getViewPaths() (paths []string) {
	dirs := []string{context.ResourcePath(), path.Join("themes", "default"), "."}
	if context.Resource != nil && context.Resource.Config != nil && context.Resource.Config.Theme != "" {
		themePath := path.Join("themes", context.Resource.Config.Theme)
		dirs = append([]string{path.Join(themePath, context.ResourcePath()), themePath}, dirs...)
	}

	for _, p := range dirs {
		for _, d := range viewPaths {
			if isExistingDir(path.Join(d, p)) {
				paths = append(paths, path.Join(d, p))
			}
		}
	}
	return paths
}

func (context *Context) findFile(layout string) (string, error) {
	for _, p := range context.getViewPaths() {
		if _, err := os.Stat(path.Join(p, layout)); !os.IsNotExist(err) {
			return path.Join(p, layout), nil
		}
	}
	return "", errors.New("file not found")
}

func (context *Context) findTemplate(tmpl *template.Template, layout string) (*template.Template, error) {
	for _, p := range context.getViewPaths() {
		if _, err := os.Stat(path.Join(p, layout)); !os.IsNotExist(err) {
			if tmpl, err = tmpl.ParseFiles(path.Join(p, layout)); err != nil {
				fmt.Println(err)
			} else {
				return tmpl, nil
			}
		}
	}
	return tmpl, errors.New("template not found")
}

func (context *Context) Render(name string, results ...interface{}) string {
	var err error
	names := strings.Split(name, "/")
	tmpl := template.New(names[len(names)-1] + ".tmpl").Funcs(context.funcMap())
	if len(results) > 0 {
		context.Result = results[0]
	}

	if tmpl, err = context.findTemplate(tmpl, name+".tmpl"); err == nil {
		var result = bytes.NewBufferString("")
		if err := tmpl.Execute(result, context); err != nil {
			fmt.Println(err)
		}
		return result.String()
	}

	return ""
}

func (context *Context) Execute(name string, result interface{}) {
	var tmpl *template.Template
	var cacheKey string

	if context.Resource != nil {
		cacheKey = path.Join(context.ResourcePath(), name)
	} else {
		cacheKey = name
	}

	if t, ok := templates[cacheKey]; !ok || true {
		var err error
		tmpl = template.New("layout.tmpl").Funcs(context.funcMap())
		if tmpl, err = context.findTemplate(tmpl, "layout.tmpl"); err == nil {
			for _, name := range []string{"header", "footer"} {
				if tmpl.Lookup(name) == nil {
					tmpl, _ = context.findTemplate(tmpl, name+".tmpl")
				}
			}
		}
	} else {
		tmpl = t
	}

	context.Content = context.Render(name, result)
	if err := tmpl.Execute(context.Writer, context); err != nil {
		fmt.Println(err)
	}
}

// Function Maps
func (context *Context) PrimaryKeyOf(value interface{}) interface{} {
	return context.GetDB().NewScope(value).PrimaryKeyValue()
}

func (context *Context) NewRecord(value interface{}) interface{} {
	return context.GetDB().NewRecord(value)
}

func (context *Context) ValueOf(value interface{}, meta *Meta) interface{} {
	return meta.Valuer(value, context.Context)
}

func (context *Context) NewResourcePath(value interface{}) string {
	if res, ok := value.(*Resource); ok {
		return path.Join(context.Admin.router.Prefix, res.ToParam(), "new")
	} else {
		return path.Join(context.Admin.router.Prefix, context.Resource.ToParam(), "new")
	}
}

func (context *Context) UrlFor(value interface{}, resources ...*Resource) string {
	var url string
	if admin, ok := value.(*Admin); ok {
		url = admin.router.Prefix
	} else if res, ok := value.(*Resource); ok {
		url = path.Join(context.Admin.router.Prefix, res.ToParam())
	} else {
		structType := reflect.Indirect(reflect.ValueOf(value)).Type().String()
		res := context.Admin.GetResource(structType)
		primaryKey := context.GetDB().NewScope(value).PrimaryKeyValue()
		url = path.Join(context.Admin.router.Prefix, res.ToParam(), fmt.Sprintf("%v", primaryKey))
	}
	return url
}

func (context *Context) LinkTo(text interface{}, link interface{}) string {
	text = reflect.Indirect(reflect.ValueOf(text)).Interface()
	if linkStr, ok := link.(string); ok {
		return fmt.Sprintf(`<a href="%v">%v</a>`, linkStr, text)
	}
	return fmt.Sprintf(`<a href="%v">%v</a>`, context.UrlFor(link), text)
}

func (context *Context) RenderForm(value interface{}, metas []*Meta) string {
	var result = bytes.NewBufferString("")
	context.renderForm(result, value, metas, []string{"QorResource"})
	return result.String()
}

func (context *Context) renderForm(result *bytes.Buffer, value interface{}, metas []*Meta, prefix []string) {
	for _, meta := range metas {
		context.RenderMeta(result, meta, value, prefix)
	}
}

func (context *Context) RenderMeta(writer *bytes.Buffer, meta *Meta, value interface{}, prefix []string) {
	prefix = append(prefix, meta.Name)

	funcsMap := context.funcMap()
	funcsMap["render_form"] = func(value interface{}, metas []*Meta, index ...int) string {
		var result = bytes.NewBufferString("")
		newPrefix := append([]string{}, prefix...)

		if len(index) > 0 {
			last := newPrefix[len(newPrefix)-1]
			newPrefix = append(newPrefix[:len(newPrefix)-1], fmt.Sprintf("%v[%v]", last, index[0]))
		}

		context.renderForm(result, value, metas, newPrefix)
		return result.String()
	}

	var tmpl = template.New(meta.Type + ".tmpl").Funcs(funcsMap)

	if tmpl, err := context.findTemplate(tmpl, fmt.Sprintf("forms/%v.tmpl", meta.Type)); err == nil {
		data := map[string]interface{}{}
		data["InputId"] = strings.Join(prefix, "")
		data["Label"] = meta.Label
		data["InputName"] = strings.Join(prefix, ".")
		value := meta.Valuer(value, context.Context)
		if rv := reflect.ValueOf(value); rv.Kind() == reflect.Ptr && !rv.IsNil() {
			value = rv.Elem().Interface()
		}
		data["Value"] = value
		if meta.GetCollection != nil {
			data["CollectionValue"] = meta.GetCollection(value, context.Context)
		}
		data["Meta"] = meta

		if err := tmpl.Execute(writer, data); err != nil {
			fmt.Println(err)
		}
	} else {
		fmt.Printf("%v: form type %v not supported\n", meta.Name, meta.Type)
	}
}

func (context *Context) HasPrimaryKey(value interface{}, primaryKey interface{}) bool {
	primaryKeys := []interface{}{}
	reflectValue := reflect.ValueOf(value)
	if reflectValue.Kind() == reflect.Ptr {
		reflectValue = reflectValue.Elem()
	}
	if reflectValue.Kind() == reflect.Slice {
		for i := 0; i < reflectValue.Len(); i++ {
			scope := &gorm.Scope{Value: reflectValue.Index(i).Interface()}
			primaryKeys = append(primaryKeys, scope.PrimaryKeyValue())
		}
	} else if reflectValue.Kind() == reflect.Struct {
		scope := &gorm.Scope{Value: value}
		primaryKeys = append(primaryKeys, scope.PrimaryKeyValue())
	}

	for _, key := range primaryKeys {
		if fmt.Sprintf("%v", primaryKey) == fmt.Sprintf("%v", key) {
			return true
		}
	}
	return false
}

func (context *Context) getResource(resources ...*Resource) *Resource {
	for _, res := range resources {
		return res
	}
	return context.Resource
}

func (context *Context) AllMetas(resources ...*Resource) []*Meta {
	return context.getResource(resources...).AllMetas()
}

func (context *Context) IndexMetas(resources ...*Resource) []*Meta {
	res := context.getResource(resources...)
	return res.AllowedMetas(res.IndexMetas(), context, roles.Read)
}

func (context *Context) EditMetas(resources ...*Resource) []*Meta {
	res := context.getResource(resources...)
	return res.AllowedMetas(res.EditMetas(), context, roles.Update)
}

func (context *Context) ShowMetas(resources ...*Resource) []*Meta {
	res := context.getResource(resources...)
	return res.AllowedMetas(res.ShowMetas(), context, roles.Read)
}

func (context *Context) NewMetas(resources ...*Resource) []*Meta {
	res := context.getResource(resources...)
	return res.AllowedMetas(res.NewMetas(), context, roles.Create)
}

func (context *Context) JavaScriptTag(name string) string {
	name = path.Join(context.Admin.GetRouter().Prefix, "assets", "javascripts", name+".js")
	return fmt.Sprintf(`<script src="%s"></script>`, name)
}

func (context *Context) StyleSheetTag(name string) string {
	name = path.Join(context.Admin.GetRouter().Prefix, "assets", "stylesheets", name+".css")
	return fmt.Sprintf(`<link type="text/css" rel="stylesheet" href="%s">`, name)
}

type Page struct {
	Page       int
	Current    bool
	IsPrevious bool
	IsNext     bool
}

func (context *Context) Pagination() []Page {
	pagination := context.Searcher.Pagination
	start := pagination.CurrentPage
	if start-5 < 1 {
		start = 1
	}

	end := start + 9
	if end > pagination.Pages {
		end = pagination.Pages
	}

	var pages []Page
	if start > 1 {
		pages = append(pages, Page{Page: start - 1, IsPrevious: true})
	}

	for i := start; i <= end; i++ {
		pages = append(pages, Page{Page: i, Current: pagination.CurrentPage == i})
	}

	if end < pagination.Pages {
		pages = append(pages, Page{Page: end + 1, IsNext: true})
	}

	return pages
}

func Equal(a, b interface{}) bool {
	return reflect.DeepEqual(a, b)
}

func (context *Context) funcMap() template.FuncMap {
	funcMap := template.FuncMap{
		"menus":             context.Admin.GetMenus,
		"current_user":      func() qor.CurrentUser { return context.CurrentUser },
		"render":            context.Render,
		"render_form":       context.RenderForm,
		"url_for":           context.UrlFor,
		"link_to":           context.LinkTo,
		"new_resource_path": context.NewResourcePath,
		"new_resource":      context.NewResource,
		"is_new_record":     context.NewRecord,
		"value_of":          context.ValueOf,
		"primary_key_of":    context.PrimaryKeyOf,
		"has_primary_key":   context.HasPrimaryKey,
		"all_metas":         context.AllMetas,
		"index_metas":       context.IndexMetas,
		"edit_metas":        context.EditMetas,
		"show_metas":        context.ShowMetas,
		"new_metas":         context.NewMetas,
		"pagination":        context.Pagination,
		"javascript_tag":    context.JavaScriptTag,
		"stylesheet_tag":    context.StyleSheetTag,
		"equal":             Equal,
		"patch_url":         context.PatchURL,
	}

	for key, value := range context.Admin.funcMaps {
		funcMap[key] = value
	}
	return funcMap
}

// PatchURL updates the query part of the current request url
//     patch_url "key" "value"
func (context *Context) PatchURL(parts ...interface{}) (u string, err error) {
	url := *context.Request.URL
	q := url.Query()
	for i := 0; i < len(parts)/2; i++ {
		key, ok := parts[i*2].(string)
		if !ok {
			err = fmt.Errorf("%[1]s type is %[1]T, want string", parts[i*2])
			return
		}
		value, ok := parts[i*2+1].(string)
		if !ok {
			err = fmt.Errorf("%[1]s type is %[1]T, want string", parts[i*2+1])
			return
		}
		if value == "" {
			q.Del(key)
		} else {
			q.Set(key, value)
		}
	}
	url.RawQuery = q.Encode()
	u = url.String()
	return
}
<|endoftext|>


<commit_before>package gexpect

import (
	"log"
	"strings"
	"testing"
)

func TestHelloWorld(*testing.T) {
	log.Printf("Testing Hello World... ")
	child, err := Spawn("echo \"Hello World\"")
	if err != nil {
		panic(err)
	}
	child.Expect("Hello World")
	log.Printf("Success\n")
}

func TestBiChannel(*testing.T) {
	log.Printf("Testing BiChannel screen... ")
	child, err := Spawn("screen")
	if err != nil {
		panic(err)
	}
	ch := child.AsyncInteractBiChannel()
	wait := func(str string) {
		for {
			msg, open := <-ch
			if !open {
				return
			}
			if strings.Contains(msg, str) {
				return
			}
		}
	}
	ch <- "echo Hello World"
	wait("Hello World")
	ch <- "times"
	wait("s")
	ch <- "^D"
	log.Printf("Success\n")

}

func TestExpectRegex(*testing.T) {
	log.Printf("Testing ExpectRegex... ")

	child, err := Spawn("/bin/sh times")
	if err != nil {
		panic(err)
	}
	child.ExpectRegex("Name")
	log.Printf("Success\n")

}

func TestExpectFtp(*testing.T) {
	log.Printf("Testing Ftp... ")

	child, err := Spawn("ftp ftp.openbsd.org")
	if err != nil {
		panic(err)
	}
	child.Expect("Name")
	child.Sendline("anonymous")
	child.Expect("Password")
	child.Sendline("pexpect@sourceforge.net")
	child.Expect("ftp> ")
	child.Sendline("cd /pub/OpenBSD/3.7/packages/i386")
	child.Expect("ftp> ")
	child.Sendline("bin")
	child.Expect("ftp> ")
	child.Sendline("prompt")
	child.Expect("ftp> ")
	child.Sendline("pwd")
	child.Expect("ftp> ")
	log.Printf("Success\n")

}

func TestInteractPing(*testing.T) {
	log.Printf("Testing Ping interact... \n")

	child, err := Spawn("ping -c8 8.8.8.8")
	if err != nil {
		panic(err)
	}
	child.Interact()
	log.Printf("Success\n")

}
<commit_msg>Expanded failure case in test<commit_after>package gexpect

import (
	"log"
	"strings"
	"testing"
)

func TestHelloWorld(*testing.T) {
	log.Printf("Testing Hello World... ")
	child, err := Spawn("echo \"Hello World\"")
	if err != nil {
		panic(err)
	}
	err = child.Expect("Hello World")
	if err != nil {
		panic(err)
	}
	log.Printf("Success\n")
}

func TestHelloWorldFailureCase(*testing.T) {
	log.Printf("Testing Hello World Failure case... ")
	child, err := Spawn("echo \"Hello World\"")
	if err != nil {
		panic(err)
	}
	err = child.Expect("YOU WILL NEVER FIND ME")
	if err != nil {
		log.Printf("Success\n")
		return
	}
	panic("Expected an error for TestHelloWorldFailureCase")
}

func TestBiChannel(*testing.T) {
	log.Printf("Testing BiChannel screen... ")
	child, err := Spawn("screen")
	if err != nil {
		panic(err)
	}
	ch := child.AsyncInteractBiChannel()
	wait := func(str string) {
		for {
			msg, open := <-ch
			if !open {
				return
			}
			if strings.Contains(msg, str) {
				return
			}
		}
	}
	ch <- "echo Hello World"
	wait("Hello World")
	ch <- "times"
	wait("s")
	ch <- "^D"
	log.Printf("Success\n")

}

func TestExpectRegex(*testing.T) {
	log.Printf("Testing ExpectRegex... ")

	child, err := Spawn("/bin/sh times")
	if err != nil {
		panic(err)
	}
	child.ExpectRegex("Name")
	log.Printf("Success\n")

}

func TestExpectFtp(*testing.T) {
	log.Printf("Testing Ftp... ")

	child, err := Spawn("ftp ftp.openbsd.org")
	if err != nil {
		panic(err)
	}
	child.Expect("Name")
	child.Sendline("anonymous")
	child.Expect("Password")
	child.Sendline("pexpect@sourceforge.net")
	child.Expect("ftp> ")
	child.Sendline("cd /pub/OpenBSD/3.7/packages/i386")
	child.Expect("ftp> ")
	child.Sendline("bin")
	child.Expect("ftp> ")
	child.Sendline("prompt")
	child.Expect("ftp> ")
	child.Sendline("pwd")
	child.Expect("ftp> ")
	log.Printf("Success\n")

}

func TestInteractPing(*testing.T) {
	log.Printf("Testing Ping interact... \n")

	child, err := Spawn("ping -c8 8.8.8.8")
	if err != nil {
		panic(err)
	}
	child.Interact()
	log.Printf("Success\n")

}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"github.com/mitchellh/go-homedir"
	"github.com/zerowidth/gh-shorthand/alfred"
	"github.com/zerowidth/gh-shorthand/config"
	"github.com/zerowidth/gh-shorthand/parser"
	"os"
	"strings"
)

func main() {
	var input string
	var items = []alfred.Item{}

	if len(os.Args) < 2 {
		input = ""
	} else {
		input = strings.Join(os.Args[1:], " ")
	}

	path, _ := homedir.Expand("~/.gh-shorthand.yml")
	cfg, err := config.LoadFromFile(path)
	if err != nil {
		items = []alfred.Item{errorItem("when loading ~/.gh-shorthand.yml", err.Error())}
		printItems(items)
		return
	}

	printItems(generateItems(cfg, input))
}

var repoIcon = octicon("repo")
var issueIcon = octicon("git-pull-request")

func generateItems(cfg *config.Config, input string) []alfred.Item {
	items := []alfred.Item{}

	if input == "" {
		return items
	}

	// input includes leading space or leading mode char followed by a space
	if len(input) > 0 {
		if input[0:1] != " " {
			return items
		}
		input = input[1:]
	}

	result := parser.Parse(cfg.RepoMap, input)
	icon := repoIcon
	usedDefault := false

	if result.Repo == "" && cfg.DefaultRepo != "" && result.Query == "" {
		result.Repo = cfg.DefaultRepo
		usedDefault = true
	}

	if result.Repo != "" && result.Query == "" {
		uid := "gh:" + result.Repo
		title := "Open " + result.Repo
		arg := "open https://github.com/" + result.Repo

		if result.Issue != "" {
			uid += "#" + result.Issue
			title += "#" + result.Issue
			arg += "/issues/" + result.Issue
			icon = issueIcon
		}

		if result.Match != "" {
			title += " (" + result.Match
			if result.Issue != "" {
				title += "#" + result.Issue
			}
			title += ")"
		}

		if usedDefault {
			title += " (default repo)"
		}

		items = append(items, alfred.Item{
			UID:   uid,
			Title: title + " on GitHub",
			Arg:   arg,
			Valid: true,
			Icon:  &icon,
		})
	}

	if !strings.Contains(input, " ") {
		for key, repo := range cfg.RepoMap {
			if strings.HasPrefix(key, input) && key != result.Match && repo != result.Repo {
				items = append(items, alfred.Item{
					UID:          "gh:" + repo,
					Title:        fmt.Sprintf("Open %s (%s) on GitHub", repo, key),
					Arg:          "open https://github.com/" + repo,
					Valid:        true,
					Autocomplete: " " + key,
					Icon:         &repoIcon,
				})
			}
		}

		if input != "" && result.Repo != input {
			items = append(items, alfred.Item{
				Title:        fmt.Sprintf("Open %s... on GitHub", input),
				Autocomplete: " " + input,
				Valid:        false,
			})
		}
	}
	return items
}

func errorItem(context, msg string) alfred.Item {
	return alfred.Item{
		Title:    fmt.Sprintf("Error %s", context),
		Subtitle: msg,
		Valid:    false,
	}
}

func printItems(items []alfred.Item) {
	doc := alfred.Items{Items: items}
	if err := json.NewEncoder(os.Stdout).Encode(doc); err != nil {
		panic(err.Error())
	}
}

// octicon is relative to the alfred workflow, so this tells alfred to retrieve
// icons from there rather than relative to this go binary.
func octicon(name string) alfred.Icon {
	return alfred.Icon{
		Path: fmt.Sprintf("octicons-%s.png", name),
	}
}
<commit_msg>Show alert icon on error<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"github.com/mitchellh/go-homedir"
	"github.com/zerowidth/gh-shorthand/alfred"
	"github.com/zerowidth/gh-shorthand/config"
	"github.com/zerowidth/gh-shorthand/parser"
	"os"
	"strings"
)

func main() {
	var input string
	var items = []alfred.Item{}

	if len(os.Args) < 2 {
		input = ""
	} else {
		input = strings.Join(os.Args[1:], " ")
	}

	path, _ := homedir.Expand("~/.gh-shorthand.yml")
	cfg, err := config.LoadFromFile(path)
	if err != nil {
		items = []alfred.Item{errorItem("when loading ~/.gh-shorthand.yml", err.Error())}
		printItems(items)
		return
	}

	printItems(generateItems(cfg, input))
}

var repoIcon = octicon("repo")
var issueIcon = octicon("git-pull-request")

func generateItems(cfg *config.Config, input string) []alfred.Item {
	items := []alfred.Item{}

	if input == "" {
		return items
	}

	// input includes leading space or leading mode char followed by a space
	if len(input) > 0 {
		if input[0:1] != " " {
			return items
		}
		input = input[1:]
	}

	result := parser.Parse(cfg.RepoMap, input)
	icon := repoIcon
	usedDefault := false

	if result.Repo == "" && cfg.DefaultRepo != "" && result.Query == "" {
		result.Repo = cfg.DefaultRepo
		usedDefault = true
	}

	if result.Repo != "" && result.Query == "" {
		uid := "gh:" + result.Repo
		title := "Open " + result.Repo
		arg := "open https://github.com/" + result.Repo

		if result.Issue != "" {
			uid += "#" + result.Issue
			title += "#" + result.Issue
			arg += "/issues/" + result.Issue
			icon = issueIcon
		}

		if result.Match != "" {
			title += " (" + result.Match
			if result.Issue != "" {
				title += "#" + result.Issue
			}
			title += ")"
		}

		if usedDefault {
			title += " (default repo)"
		}

		items = append(items, alfred.Item{
			UID:   uid,
			Title: title + " on GitHub",
			Arg:   arg,
			Valid: true,
			Icon:  icon,
		})
	}

	if !strings.Contains(input, " ") {
		for key, repo := range cfg.RepoMap {
			if strings.HasPrefix(key, input) && key != result.Match && repo != result.Repo {
				items = append(items, alfred.Item{
					UID:          "gh:" + repo,
					Title:        fmt.Sprintf("Open %s (%s) on GitHub", repo, key),
					Arg:          "open https://github.com/" + repo,
					Valid:        true,
					Autocomplete: " " + key,
					Icon:         repoIcon,
				})
			}
		}

		if input != "" && result.Repo != input {
			items = append(items, alfred.Item{
				Title:        fmt.Sprintf("Open %s... on GitHub", input),
				Autocomplete: " " + input,
				Valid:        false,
			})
		}
	}
	return items
}

func errorItem(context, msg string) alfred.Item {
	return alfred.Item{
		Title:    fmt.Sprintf("Error %s", context),
		Subtitle: msg,
		Icon:     octicon("alert"),
		Valid:    false,
	}
}

func printItems(items []alfred.Item) {
	doc := alfred.Items{Items: items}
	if err := json.NewEncoder(os.Stdout).Encode(doc); err != nil {
		panic(err.Error())
	}
}

// octicon is relative to the alfred workflow, so this tells alfred to retrieve
// icons from there rather than relative to this go binary.
func octicon(name string) *alfred.Icon {
	return &alfred.Icon{
		Path: fmt.Sprintf("octicons-%s.png", name),
	}
}
<|endoftext|>


<commit_before>package cfg

import (
	"crypto/sha256"
	"fmt"
	"io/ioutil"
	"sort"
	"strings"
	"time"

	"gopkg.in/yaml.v2"
)

// Lockfile represents a glide.lock file.
type Lockfile struct {
	Hash       string    `yaml:"hash"`
	Updated    time.Time `yaml:"updated"`
	Imports    Locks     `yaml:"imports"`
	DevImports Locks     `yaml:"devImports"`
}

// LockfileFromYaml returns an instance of Lockfile from YAML
func LockfileFromYaml(yml []byte) (*Lockfile, error) {
	lock := &Lockfile{}
	err := yaml.Unmarshal([]byte(yml), &lock)
	return lock, err
}

// Marshal converts a Config instance to YAML
func (lf *Lockfile) Marshal() ([]byte, error) {
	yml, err := yaml.Marshal(&lf)
	if err != nil {
		return []byte{}, err
	}
	return yml, nil
}

// WriteFile writes a Glide lock file.
//
// This is a convenience function that marshals the YAML and then writes it to
// the given file. If the file exists, it will be clobbered.
func (lf *Lockfile) WriteFile(lockpath string) error {
	o, err := lf.Marshal()
	if err != nil {
		return err
	}
	return ioutil.WriteFile(lockpath, o, 0666)
}

// Clone returns a clone of Lockfile
func (lf *Lockfile) Clone() *Lockfile {
	n := &Lockfile{}
	n.Hash = lf.Hash
	n.Updated = lf.Updated
	n.Imports = lf.Imports.Clone()
	n.DevImports = lf.DevImports.Clone()

	return n
}

// Fingerprint returns a hash of the contents minus the date. This allows for
// two lockfiles to be compared irrespective of their updated times.
func (lf *Lockfile) Fingerprint() (string, error) {
	c := lf.Clone()
	c.Updated = time.Time{} // Set the time to be the nil equivalent
	sort.Sort(c.Imports)
	sort.Sort(c.DevImports)
	yml, err := c.Marshal()
	if err != nil {
		return "", err
	}

	hash := sha256.New()
	hash.Write(yml)
	return fmt.Sprintf("%x", hash.Sum(nil)), nil
}

// Locks is a slice of locked dependencies.
type Locks []*Lock

// Clone returns a Clone of Locks.
func (l Locks) Clone() Locks {
	n := make(Locks, 0, len(l))
	for _, v := range l {
		n = append(n, v.Clone())
	}
	return n
}

// Len returns the length of the Locks. This is needed for sorting with
// the sort package.
func (l Locks) Len() int {
	return len(l)
}

// Less is needed for the sort interface. It compares two locks based on
// their name.
func (l Locks) Less(i, j int) bool {

	// Names are normalized to lowercase because case affects sorting order. For
	// example, Masterminds comes before kylelemons. Making them lowercase
	// causes kylelemons to come first which is what is expected.
	return strings.ToLower(l[i].Name) < strings.ToLower(l[j].Name)
}

// Swap is needed for the sort interface. It swaps the position of two
// locks.
func (l Locks) Swap(i, j int) {
	l[i], l[j] = l[j], l[i]
}

// Lock represents an individual locked dependency.
type Lock struct {
	Name        string   `yaml:"name"`
	Version     string   `yaml:"version"`
	Repository  string   `yaml:"repo,omitempty"`
	VcsType     string   `yaml:"vcs,omitempty"`
	Subpackages []string `yaml:"subpackages,omitempty"`
	Arch        []string `yaml:"arch,omitempty"`
	Os          []string `yaml:"os,omitempty"`
}

// Clone creates a clone of a Lock.
func (l *Lock) Clone() *Lock {
	return &Lock{
		Name:        l.Name,
		Version:     l.Version,
		Repository:  l.Repository,
		VcsType:     l.VcsType,
		Subpackages: l.Subpackages,
		Arch:        l.Arch,
		Os:          l.Os,
	}
}

// NewLockfile is used to create an instance of Lockfile.
func NewLockfile(ds Dependencies, hash string) *Lockfile {
	lf := &Lockfile{
		Hash:    hash,
		Updated: time.Now(),
		Imports: make([]*Lock, len(ds)),
	}

	for i := 0; i < len(ds); i++ {
		lf.Imports[i] = &Lock{
			Name:        ds[i].Name,
			Version:     ds[i].Pin,
			Repository:  ds[i].Repository,
			VcsType:     ds[i].VcsType,
			Subpackages: ds[i].Subpackages,
			Arch:        ds[i].Arch,
			Os:          ds[i].Os,
		}
	}

	sort.Sort(lf.Imports)

	return lf
}

// LockfileFromMap takes a map of dependencies and generates a lock Lockfile instance.
func LockfileFromMap(ds map[string]*Dependency, hash string) *Lockfile {
	lf := &Lockfile{
		Hash:    hash,
		Updated: time.Now(),
		Imports: make([]*Lock, len(ds)),
	}

	i := 0
	for name, dep := range ds {
		lf.Imports[i] = &Lock{
			Name:        name,
			Version:     dep.Pin,
			Repository:  dep.Repository,
			VcsType:     dep.VcsType,
			Subpackages: dep.Subpackages,
			Arch:        dep.Arch,
			Os:          dep.Os,
		}
		i++
	}

	sort.Sort(lf.Imports)

	return lf
}
<commit_msg>Updating fingerprint to the same format as the standard library<commit_after>package cfg

import (
	"crypto/sha256"
	"io/ioutil"
	"sort"
	"strings"
	"time"

	"gopkg.in/yaml.v2"
)

// Lockfile represents a glide.lock file.
type Lockfile struct {
	Hash       string    `yaml:"hash"`
	Updated    time.Time `yaml:"updated"`
	Imports    Locks     `yaml:"imports"`
	DevImports Locks     `yaml:"devImports"`
}

// LockfileFromYaml returns an instance of Lockfile from YAML
func LockfileFromYaml(yml []byte) (*Lockfile, error) {
	lock := &Lockfile{}
	err := yaml.Unmarshal([]byte(yml), &lock)
	return lock, err
}

// Marshal converts a Config instance to YAML
func (lf *Lockfile) Marshal() ([]byte, error) {
	yml, err := yaml.Marshal(&lf)
	if err != nil {
		return []byte{}, err
	}
	return yml, nil
}

// WriteFile writes a Glide lock file.
//
// This is a convenience function that marshals the YAML and then writes it to
// the given file. If the file exists, it will be clobbered.
func (lf *Lockfile) WriteFile(lockpath string) error {
	o, err := lf.Marshal()
	if err != nil {
		return err
	}
	return ioutil.WriteFile(lockpath, o, 0666)
}

// Clone returns a clone of Lockfile
func (lf *Lockfile) Clone() *Lockfile {
	n := &Lockfile{}
	n.Hash = lf.Hash
	n.Updated = lf.Updated
	n.Imports = lf.Imports.Clone()
	n.DevImports = lf.DevImports.Clone()

	return n
}

// Fingerprint returns a hash of the contents minus the date. This allows for
// two lockfiles to be compared irrespective of their updated times.
func (lf *Lockfile) Fingerprint() ([32]byte, error) {
	c := lf.Clone()
	c.Updated = time.Time{} // Set the time to be the nil equivalent
	sort.Sort(c.Imports)
	sort.Sort(c.DevImports)
	yml, err := c.Marshal()
	if err != nil {
		return [32]byte{}, err
	}

	return sha256.Sum256(yml), nil
}

// Locks is a slice of locked dependencies.
type Locks []*Lock

// Clone returns a Clone of Locks.
func (l Locks) Clone() Locks {
	n := make(Locks, 0, len(l))
	for _, v := range l {
		n = append(n, v.Clone())
	}
	return n
}

// Len returns the length of the Locks. This is needed for sorting with
// the sort package.
func (l Locks) Len() int {
	return len(l)
}

// Less is needed for the sort interface. It compares two locks based on
// their name.
func (l Locks) Less(i, j int) bool {

	// Names are normalized to lowercase because case affects sorting order. For
	// example, Masterminds comes before kylelemons. Making them lowercase
	// causes kylelemons to come first which is what is expected.
	return strings.ToLower(l[i].Name) < strings.ToLower(l[j].Name)
}

// Swap is needed for the sort interface. It swaps the position of two
// locks.
func (l Locks) Swap(i, j int) {
	l[i], l[j] = l[j], l[i]
}

// Lock represents an individual locked dependency.
type Lock struct {
	Name        string   `yaml:"name"`
	Version     string   `yaml:"version"`
	Repository  string   `yaml:"repo,omitempty"`
	VcsType     string   `yaml:"vcs,omitempty"`
	Subpackages []string `yaml:"subpackages,omitempty"`
	Arch        []string `yaml:"arch,omitempty"`
	Os          []string `yaml:"os,omitempty"`
}

// Clone creates a clone of a Lock.
func (l *Lock) Clone() *Lock {
	return &Lock{
		Name:        l.Name,
		Version:     l.Version,
		Repository:  l.Repository,
		VcsType:     l.VcsType,
		Subpackages: l.Subpackages,
		Arch:        l.Arch,
		Os:          l.Os,
	}
}

// NewLockfile is used to create an instance of Lockfile.
func NewLockfile(ds Dependencies, hash string) *Lockfile {
	lf := &Lockfile{
		Hash:    hash,
		Updated: time.Now(),
		Imports: make([]*Lock, len(ds)),
	}

	for i := 0; i < len(ds); i++ {
		lf.Imports[i] = &Lock{
			Name:        ds[i].Name,
			Version:     ds[i].Pin,
			Repository:  ds[i].Repository,
			VcsType:     ds[i].VcsType,
			Subpackages: ds[i].Subpackages,
			Arch:        ds[i].Arch,
			Os:          ds[i].Os,
		}
	}

	sort.Sort(lf.Imports)

	return lf
}

// LockfileFromMap takes a map of dependencies and generates a lock Lockfile instance.
func LockfileFromMap(ds map[string]*Dependency, hash string) *Lockfile {
	lf := &Lockfile{
		Hash:    hash,
		Updated: time.Now(),
		Imports: make([]*Lock, len(ds)),
	}

	i := 0
	for name, dep := range ds {
		lf.Imports[i] = &Lock{
			Name:        name,
			Version:     dep.Pin,
			Repository:  dep.Repository,
			VcsType:     dep.VcsType,
			Subpackages: dep.Subpackages,
			Arch:        dep.Arch,
			Os:          dep.Os,
		}
		i++
	}

	sort.Sort(lf.Imports)

	return lf
}
<|endoftext|>


<commit_before>// bulk_load_influx loads an InfluxDB daemon with data from stdin.
//
// The caller is responsible for assuring that the database is empty before
// bulk load.
package main

import (
	"bufio"
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"strings"
	"sync"
	"sync/atomic"
	"time"

	"github.com/influxdata/influxdb-comparisons/util/telemetry"
	"github.com/pkg/profile"
	"github.com/valyala/fasthttp"
)

// Program option vars:
var (
	csvDaemonUrls           string
	daemonUrls              []string
	dbName                  string
	replicationFactor       int
	workers                 int
	itemLimit               int64
	batchSize               int
	backoff                 time.Duration
	timeLimit               time.Duration
	progressInterval        time.Duration
	doLoad                  bool
	doDBCreate              bool
	useGzip                 bool
	doAbortOnExist          bool
	memprofile              bool
	telemetryHost           string
	telemetryStderr         bool
	telemetryBatchSize      uint
	telemetryExperimentName string
)

// Global vars
var (
	bufPool             sync.Pool
	batchChan           chan *bytes.Buffer
	inputDone           chan struct{}
	workersGroup        sync.WaitGroup
	backingOffChans     []chan bool
	backingOffDones     []chan struct{}
	telemetryChanPoints chan *telemetry.Point
	telemetryChanDone   chan struct{}

	progressIntervalItems uint64
)

// Parse args:
func init() {
	flag.StringVar(&csvDaemonUrls, "urls", "http://localhost:8086", "InfluxDB URLs, comma-separated. Will be used in a round-robin fashion.")
	flag.StringVar(&dbName, "db", "benchmark_db", "Database name.")
	flag.IntVar(&replicationFactor, "replication-factor", 2, "Cluster replication factor (only applies to clustered databases).")
	flag.IntVar(&batchSize, "batch-size", 5000, "Batch size (1 line of input = 1 item).")
	flag.IntVar(&workers, "workers", 1, "Number of parallel requests to make.")
	flag.Int64Var(&itemLimit, "item-limit", -1, "Number of items to read from stdin before quitting. (1 item per 1 line of input.)")
	flag.DurationVar(&backoff, "backoff", time.Second, "Time to sleep between requests when server indicates backpressure is needed.")
	flag.DurationVar(&timeLimit, "time-limit", -1, "Maximum duration to run (-1 is the default: no limit).")
	flag.DurationVar(&progressInterval, "progress-interval", -1, "Duration between printing progress messages.")
	flag.BoolVar(&useGzip, "gzip", true, "Whether to gzip encode requests (default true).")
	flag.BoolVar(&doLoad, "do-load", true, "Whether to write data. Set this flag to false to check input read speed.")
	flag.BoolVar(&doDBCreate, "do-db-create", true, "Whether to create the database.")
	flag.BoolVar(&doAbortOnExist, "do-abort-on-exist", true, "Whether to abort if the destination database already exists.")
	flag.BoolVar(&memprofile, "memprofile", false, "Whether to write a memprofile (file automatically determined).")
	flag.StringVar(&telemetryHost, "telemetry-host", "", "InfluxDB host to write telegraf telemetry to (optional).")
	flag.StringVar(&telemetryExperimentName, "telemetry-experiment-name", "unnamed_experiment", "Experiment name for telemetry.")
	flag.BoolVar(&telemetryStderr, "telemetry-stderr", false, "Whether to write telemetry also to stderr.")
	flag.UintVar(&telemetryBatchSize, "telemetry-batch-size", 10, "Telemetry batch size (lines).")

	flag.Parse()

	daemonUrls = strings.Split(csvDaemonUrls, ",")
	if len(daemonUrls) == 0 {
		log.Fatal("missing 'urls' flag")
	}
	fmt.Printf("daemon URLs: %v\n", daemonUrls)
	fmt.Printf("telemetry destination: %v\n", telemetryHost)

	if telemetryHost != "" {
		if telemetryBatchSize == 0 {
			panic("invalid telemetryBatchSize")
		}
	}
}

func main() {
	if memprofile {
		p := profile.Start(profile.MemProfile)
		defer p.Stop()
	}
	if doLoad && doDBCreate {
		// check that there are no pre-existing databases:
		existingDatabases, err := listDatabases(daemonUrls[0])
		if err != nil {
			log.Fatal(err)
		}

		if len(existingDatabases) > 0 {
			if doAbortOnExist {
				log.Fatalf("There are databases already in the data store. If you know what you are doing, run the command:\ncurl 'http://localhost:8086/query?q=drop%%20database%%20%s'\n", existingDatabases[0])
			} else {
				log.Printf("Info: there are databases already in the data store.")
			}
		}

		if len(existingDatabases) == 0 {
			err = createDb(daemonUrls[0], dbName, replicationFactor)
			if err != nil {
				log.Fatal(err)
			}
			time.Sleep(1000 * time.Millisecond)
		}
	}

	bufPool = sync.Pool{
		New: func() interface{} {
			return bytes.NewBuffer(make([]byte, 0, 4*1024*1024))
		},
	}

	batchChan = make(chan *bytes.Buffer, workers)
	inputDone = make(chan struct{})

	backingOffChans = make([]chan bool, workers)
	backingOffDones = make([]chan struct{}, workers)

	if telemetryHost != "" {
		telemetryCollector := telemetry.NewCollector(telemetryHost, "telegraf")
		telemetryChanPoints, telemetryChanDone = telemetry.EZRunAsync(telemetryCollector, telemetryBatchSize, telemetryExperimentName, telemetryStderr)
	}

	for i := 0; i < workers; i++ {
		daemonUrl := daemonUrls[i%len(daemonUrls)]
		backingOffChans[i] = make(chan bool, 100)
		backingOffDones[i] = make(chan struct{})
		workersGroup.Add(1)
		cfg := HTTPWriterConfig{
			DebugInfo:      fmt.Sprintf("worker #%d, dest url: %s", i, daemonUrl),
			Host:           daemonUrl,
			Database:       dbName,
			BackingOffChan: backingOffChans[i],
			BackingOffDone: backingOffDones[i],
		}
		go processBatches(NewHTTPWriter(cfg), backingOffChans[i], backingOffDones[i], telemetryChanPoints, fmt.Sprintf("%d", i))
		go processBackoffMessages(i, backingOffChans[i], backingOffDones[i])
	}

	if progressInterval >= 0 {
		go func() {
			start := time.Now()
			for end := range time.NewTicker(progressInterval).C {
				n := atomic.SwapUint64(&progressIntervalItems, 0)

				//absoluteMillis := end.Add(-progressInterval).UnixNano() / 1e6
				absoluteMillis := start.UTC().UnixNano() / 1e6
				fmt.Printf("[interval_progress_items] %dms, %d\n", absoluteMillis, n)
				start = end
			}
		}()
	}

	start := time.Now()
	itemsRead, bytesRead := scan(batchSize)

	<-inputDone
	close(batchChan)

	workersGroup.Wait()

	for i := range backingOffChans {
		close(backingOffChans[i])
		<-backingOffDones[i]
	}

	end := time.Now()
	took := end.Sub(start)
	itemsRate := float64(itemsRead) / float64(took.Seconds())
	bytesRate := float64(bytesRead) / float64(took.Seconds())

	if telemetryHost != "" {
		close(telemetryChanPoints)
		<-telemetryChanDone
	}

	fmt.Printf("loaded %d items in %fsec with %d workers (mean rate %f/sec, %.2fMB/sec from stdin)\n", itemsRead, took.Seconds(), workers, itemsRate, bytesRate/(1<<20))
}

// scan reads one item at a time from stdin. 1 item = 1 line.
// When the requested number of items per batch is met, send a batch over batchChan for the workers to write.
func scan(itemsPerBatch int) (int64, int64) {
	buf := bufPool.Get().(*bytes.Buffer)

	var n int
	var itemsRead, bytesRead int64
	newline := []byte("\n")
	var deadline time.Time
	if timeLimit >= 0 {
		deadline = time.Now().Add(timeLimit)
	}

	var batchItemCount uint64

	scanner := bufio.NewScanner(bufio.NewReaderSize(os.Stdin, 4*1024*1024))
outer:
	for scanner.Scan() {
		if itemsRead == itemLimit {
			break
		}

		itemsRead++
		batchItemCount++

		buf.Write(scanner.Bytes())
		buf.Write(newline)

		n++
		if n >= itemsPerBatch {
			atomic.AddUint64(&progressIntervalItems, batchItemCount)
			batchItemCount = 0

			bytesRead += int64(buf.Len())
			batchChan <- buf
			buf = bufPool.Get().(*bytes.Buffer)
			n = 0

			if timeLimit >= 0 && time.Now().After(deadline) {
				break outer
			}

		}
	}

	if err := scanner.Err(); err != nil {
		log.Fatalf("Error reading input: %s", err.Error())
	}

	// Finished reading input, make sure last batch goes out.
	if n > 0 {
		batchChan <- buf
	}

	// Closing inputDone signals to the application that we've read everything and can now shut down.
	close(inputDone)

	return itemsRead, bytesRead
}

// processBatches reads byte buffers from batchChan and writes them to the target server, while tracking stats on the write.
func processBatches(w *HTTPWriter, backoffSrc chan bool, backoffDst chan struct{}, telemetrySink chan *telemetry.Point, telemetryWorkerLabel string) {
	batchesSeen := 0
	for batch := range batchChan {
		batchesSeen++

		var bodySize int

		// Write the batch: try until backoff is not needed.
		if doLoad {
			var err error
			for {
				if useGzip {
					compressedBatch := bufPool.Get().(*bytes.Buffer)
					fasthttp.WriteGzip(compressedBatch, batch.Bytes())
					bodySize = len(compressedBatch.Bytes())
					_, err = w.WriteLineProtocol(compressedBatch.Bytes(), true)
					// Return the compressed batch buffer to the pool.
					compressedBatch.Reset()
					bufPool.Put(compressedBatch)
				} else {
					bodySize = len(batch.Bytes())
					_, err = w.WriteLineProtocol(batch.Bytes(), false)
				}

				if err == BackoffError {
					backoffSrc <- true
					time.Sleep(backoff)
				} else {
					backoffSrc <- false
					break
				}
			}
			if err != nil {
				log.Fatalf("Error writing: %s\n", err.Error())
			}
		}

		// Return the batch buffer to the pool.
		batch.Reset()
		bufPool.Put(batch)

		// Report telemetry, if applicable:
		if telemetrySink != nil {
			p := telemetry.GetPointFromGlobalPool()
			p.Init("benchmark_write", 0)
			p.AddTag("worker_id", telemetryWorkerLabel)
			p.AddTag("worker_req_num", fmt.Sprintf("%d", batchesSeen))
			p.AddBoolField("gzip", useGzip)
			p.AddInt64Field("body_bytes", int64(bodySize))
			telemetrySink <- p
		}
	}
	workersGroup.Done()
}

func processBackoffMessages(workerId int, src chan bool, dst chan struct{}) {
	var totalBackoffSecs float64
	var start time.Time
	last := false
	for this := range src {
		if this && !last {
			start = time.Now()
			last = true
		} else if !this && last {
			took := time.Now().Sub(start)
			fmt.Printf("[worker %d] backoff took %.02fsec\n", workerId, took.Seconds())
			totalBackoffSecs += took.Seconds()
			last = false
			start = time.Now()
		}
	}
	fmt.Printf("[worker %d] backoffs took a total of %fsec of runtime\n", workerId, totalBackoffSecs)
	dst <- struct{}{}
}

func createDb(daemon_url, dbname string, replicationFactor int) error {
	u, err := url.Parse(daemon_url)
	if err != nil {
		return err
	}

	// serialize params the right way:
	u.Path = "query"
	v := u.Query()
	v.Set("consistency", "all")
	v.Set("q", fmt.Sprintf("CREATE DATABASE %s WITH REPLICATION %d", dbname, replicationFactor))
	u.RawQuery = v.Encode()

	req, err := http.NewRequest("GET", u.String(), nil)
	if err != nil {
		return err
	}

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	// does the body need to be read into the void?

	if resp.StatusCode != 200 {
		return fmt.Errorf("bad db create")
	}
	return nil
}

// listDatabases lists the existing databases in InfluxDB.
func listDatabases(daemonUrl string) ([]string, error) {
	u := fmt.Sprintf("%s/query?q=show%%20databases", daemonUrl)
	resp, err := http.Get(u)
	if err != nil {
		return nil, fmt.Errorf("listDatabases error: %s", err.Error())
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	// Do ad-hoc parsing to find existing database names:
	// {"results":[{"series":[{"name":"databases","columns":["name"],"values":[["_internal"],["benchmark_db"]]}]}]}%
	type listingType struct {
		Results []struct {
			Series []struct {
				Values [][]string
			}
		}
	}
	var listing listingType
	err = json.Unmarshal(body, &listing)
	if err != nil {
		return nil, err
	}

	ret := []string{}
	for _, nestedName := range listing.Results[0].Series[0].Values {
		name := nestedName[0]
		// the _internal database is skipped:
		if name == "_internal" {
			continue
		}
		ret = append(ret, name)
	}
	return ret, nil
}
<commit_msg>Update influx load telemetry<commit_after>// bulk_load_influx loads an InfluxDB daemon with data from stdin.
//
// The caller is responsible for assuring that the database is empty before
// bulk load.
package main

import (
	"bufio"
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"strings"
	"sync"
	"sync/atomic"
	"time"

	"github.com/influxdata/influxdb-comparisons/util/telemetry"
	"github.com/pkg/profile"
	"github.com/valyala/fasthttp"
)

// Program option vars:
var (
	csvDaemonUrls           string
	daemonUrls              []string
	dbName                  string
	replicationFactor       int
	workers                 int
	itemLimit               int64
	batchSize               int
	backoff                 time.Duration
	timeLimit               time.Duration
	progressInterval        time.Duration
	doLoad                  bool
	doDBCreate              bool
	useGzip                 bool
	doAbortOnExist          bool
	memprofile              bool
	telemetryHost           string
	telemetryStderr         bool
	telemetryBatchSize      uint
	telemetryExperimentName string
)

// Global vars
var (
	bufPool             sync.Pool
	batchChan           chan *bytes.Buffer
	inputDone           chan struct{}
	workersGroup        sync.WaitGroup
	backingOffChans     []chan bool
	backingOffDones     []chan struct{}
	telemetryChanPoints chan *telemetry.Point
	telemetryChanDone   chan struct{}

	progressIntervalItems uint64
)

// Parse args:
func init() {
	flag.StringVar(&csvDaemonUrls, "urls", "http://localhost:8086", "InfluxDB URLs, comma-separated. Will be used in a round-robin fashion.")
	flag.StringVar(&dbName, "db", "benchmark_db", "Database name.")
	flag.IntVar(&replicationFactor, "replication-factor", 2, "Cluster replication factor (only applies to clustered databases).")
	flag.IntVar(&batchSize, "batch-size", 5000, "Batch size (1 line of input = 1 item).")
	flag.IntVar(&workers, "workers", 1, "Number of parallel requests to make.")
	flag.Int64Var(&itemLimit, "item-limit", -1, "Number of items to read from stdin before quitting. (1 item per 1 line of input.)")
	flag.DurationVar(&backoff, "backoff", time.Second, "Time to sleep between requests when server indicates backpressure is needed.")
	flag.DurationVar(&timeLimit, "time-limit", -1, "Maximum duration to run (-1 is the default: no limit).")
	flag.DurationVar(&progressInterval, "progress-interval", -1, "Duration between printing progress messages.")
	flag.BoolVar(&useGzip, "gzip", true, "Whether to gzip encode requests (default true).")
	flag.BoolVar(&doLoad, "do-load", true, "Whether to write data. Set this flag to false to check input read speed.")
	flag.BoolVar(&doDBCreate, "do-db-create", true, "Whether to create the database.")
	flag.BoolVar(&doAbortOnExist, "do-abort-on-exist", true, "Whether to abort if the destination database already exists.")
	flag.BoolVar(&memprofile, "memprofile", false, "Whether to write a memprofile (file automatically determined).")
	flag.StringVar(&telemetryHost, "telemetry-host", "", "InfluxDB host to write telegraf telemetry to (optional).")
	flag.StringVar(&telemetryExperimentName, "telemetry-experiment-name", "unnamed_experiment", "Experiment name for telemetry.")
	flag.BoolVar(&telemetryStderr, "telemetry-stderr", false, "Whether to write telemetry also to stderr.")
	flag.UintVar(&telemetryBatchSize, "telemetry-batch-size", 10, "Telemetry batch size (lines).")

	flag.Parse()

	daemonUrls = strings.Split(csvDaemonUrls, ",")
	if len(daemonUrls) == 0 {
		log.Fatal("missing 'urls' flag")
	}
	fmt.Printf("daemon URLs: %v\n", daemonUrls)

	if telemetryHost != "" {
		fmt.Printf("telemetry destination: %v\n", telemetryHost)
		if telemetryBatchSize == 0 {
			panic("invalid telemetryBatchSize")
		}
	}
}

func main() {
	if memprofile {
		p := profile.Start(profile.MemProfile)
		defer p.Stop()
	}
	if doLoad && doDBCreate {
		// check that there are no pre-existing databases:
		existingDatabases, err := listDatabases(daemonUrls[0])
		if err != nil {
			log.Fatal(err)
		}

		if len(existingDatabases) > 0 {
			if doAbortOnExist {
				log.Fatalf("There are databases already in the data store. If you know what you are doing, run the command:\ncurl 'http://localhost:8086/query?q=drop%%20database%%20%s'\n", existingDatabases[0])
			} else {
				log.Printf("Info: there are databases already in the data store.")
			}
		}

		if len(existingDatabases) == 0 {
			err = createDb(daemonUrls[0], dbName, replicationFactor)
			if err != nil {
				log.Fatal(err)
			}
			time.Sleep(1000 * time.Millisecond)
		}
	}

	bufPool = sync.Pool{
		New: func() interface{} {
			return bytes.NewBuffer(make([]byte, 0, 4*1024*1024))
		},
	}

	batchChan = make(chan *bytes.Buffer, workers)
	inputDone = make(chan struct{})

	backingOffChans = make([]chan bool, workers)
	backingOffDones = make([]chan struct{}, workers)

	if telemetryHost != "" {
		telemetryCollector := telemetry.NewCollector(telemetryHost, "telegraf")
		telemetryChanPoints, telemetryChanDone = telemetry.EZRunAsync(telemetryCollector, telemetryBatchSize, telemetryExperimentName, telemetryStderr)
	}

	for i := 0; i < workers; i++ {
		daemonUrl := daemonUrls[i%len(daemonUrls)]
		backingOffChans[i] = make(chan bool, 100)
		backingOffDones[i] = make(chan struct{})
		workersGroup.Add(1)
		cfg := HTTPWriterConfig{
			DebugInfo:      fmt.Sprintf("worker #%d, dest url: %s", i, daemonUrl),
			Host:           daemonUrl,
			Database:       dbName,
			BackingOffChan: backingOffChans[i],
			BackingOffDone: backingOffDones[i],
		}
		go processBatches(NewHTTPWriter(cfg), backingOffChans[i], backingOffDones[i], telemetryChanPoints, fmt.Sprintf("%d", i))
		go processBackoffMessages(i, backingOffChans[i], backingOffDones[i])
	}

	if progressInterval >= 0 {
		go func() {
			start := time.Now()
			for end := range time.NewTicker(progressInterval).C {
				n := atomic.SwapUint64(&progressIntervalItems, 0)

				//absoluteMillis := end.Add(-progressInterval).UnixNano() / 1e6
				absoluteMillis := start.UTC().UnixNano() / 1e6
				fmt.Printf("[interval_progress_items] %dms, %d\n", absoluteMillis, n)
				start = end
			}
		}()
	}

	start := time.Now()
	itemsRead, bytesRead := scan(batchSize)

	<-inputDone
	close(batchChan)

	workersGroup.Wait()

	for i := range backingOffChans {
		close(backingOffChans[i])
		<-backingOffDones[i]
	}

	end := time.Now()
	took := end.Sub(start)
	itemsRate := float64(itemsRead) / float64(took.Seconds())
	bytesRate := float64(bytesRead) / float64(took.Seconds())

	if telemetryHost != "" {
		close(telemetryChanPoints)
		<-telemetryChanDone
	}

	fmt.Printf("loaded %d items in %fsec with %d workers (mean rate %f/sec, %.2fMB/sec from stdin)\n", itemsRead, took.Seconds(), workers, itemsRate, bytesRate/(1<<20))
}

// scan reads one item at a time from stdin. 1 item = 1 line.
// When the requested number of items per batch is met, send a batch over batchChan for the workers to write.
func scan(itemsPerBatch int) (int64, int64) {
	buf := bufPool.Get().(*bytes.Buffer)

	var n int
	var itemsRead, bytesRead int64
	newline := []byte("\n")
	var deadline time.Time
	if timeLimit >= 0 {
		deadline = time.Now().Add(timeLimit)
	}

	var batchItemCount uint64

	scanner := bufio.NewScanner(bufio.NewReaderSize(os.Stdin, 4*1024*1024))
outer:
	for scanner.Scan() {
		if itemsRead == itemLimit {
			break
		}

		itemsRead++
		batchItemCount++

		buf.Write(scanner.Bytes())
		buf.Write(newline)

		n++
		if n >= itemsPerBatch {
			atomic.AddUint64(&progressIntervalItems, batchItemCount)
			batchItemCount = 0

			bytesRead += int64(buf.Len())
			batchChan <- buf
			buf = bufPool.Get().(*bytes.Buffer)
			n = 0

			if timeLimit >= 0 && time.Now().After(deadline) {
				break outer
			}

		}
	}

	if err := scanner.Err(); err != nil {
		log.Fatalf("Error reading input: %s", err.Error())
	}

	// Finished reading input, make sure last batch goes out.
	if n > 0 {
		batchChan <- buf
	}

	// Closing inputDone signals to the application that we've read everything and can now shut down.
	close(inputDone)

	return itemsRead, bytesRead
}

// processBatches reads byte buffers from batchChan and writes them to the target server, while tracking stats on the write.
func processBatches(w *HTTPWriter, backoffSrc chan bool, backoffDst chan struct{}, telemetrySink chan *telemetry.Point, telemetryWorkerLabel string) {
	var batchesSeen int64
	for batch := range batchChan {
		batchesSeen++

		var bodySize int

		// Write the batch: try until backoff is not needed.
		if doLoad {
			var err error
			for {
				if useGzip {
					compressedBatch := bufPool.Get().(*bytes.Buffer)
					fasthttp.WriteGzip(compressedBatch, batch.Bytes())
					bodySize = len(compressedBatch.Bytes())
					_, err = w.WriteLineProtocol(compressedBatch.Bytes(), true)
					// Return the compressed batch buffer to the pool.
					compressedBatch.Reset()
					bufPool.Put(compressedBatch)
				} else {
					bodySize = len(batch.Bytes())
					_, err = w.WriteLineProtocol(batch.Bytes(), false)
				}

				if err == BackoffError {
					backoffSrc <- true
					time.Sleep(backoff)
				} else {
					backoffSrc <- false
					break
				}
			}
			if err != nil {
				log.Fatalf("Error writing: %s\n", err.Error())
			}
		}

		// Return the batch buffer to the pool.
		batch.Reset()
		bufPool.Put(batch)

		// Report telemetry, if applicable:
		if telemetrySink != nil {
			p := telemetry.GetPointFromGlobalPool()
			p.Init("benchmark_write", time.Now().UnixNano())
			p.AddTag("worker_id", telemetryWorkerLabel)
			p.AddInt64Field("worker_req_num", batchesSeen)
			p.AddBoolField("gzip", useGzip)
			p.AddInt64Field("body_bytes", int64(bodySize))
			telemetrySink <- p
		}
	}
	workersGroup.Done()
}

func processBackoffMessages(workerId int, src chan bool, dst chan struct{}) {
	var totalBackoffSecs float64
	var start time.Time
	last := false
	for this := range src {
		if this && !last {
			start = time.Now()
			last = true
		} else if !this && last {
			took := time.Now().Sub(start)
			fmt.Printf("[worker %d] backoff took %.02fsec\n", workerId, took.Seconds())
			totalBackoffSecs += took.Seconds()
			last = false
			start = time.Now()
		}
	}
	fmt.Printf("[worker %d] backoffs took a total of %fsec of runtime\n", workerId, totalBackoffSecs)
	dst <- struct{}{}
}

func createDb(daemon_url, dbname string, replicationFactor int) error {
	u, err := url.Parse(daemon_url)
	if err != nil {
		return err
	}

	// serialize params the right way:
	u.Path = "query"
	v := u.Query()
	v.Set("consistency", "all")
	v.Set("q", fmt.Sprintf("CREATE DATABASE %s WITH REPLICATION %d", dbname, replicationFactor))
	u.RawQuery = v.Encode()

	req, err := http.NewRequest("GET", u.String(), nil)
	if err != nil {
		return err
	}

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	// does the body need to be read into the void?

	if resp.StatusCode != 200 {
		return fmt.Errorf("bad db create")
	}
	return nil
}

// listDatabases lists the existing databases in InfluxDB.
func listDatabases(daemonUrl string) ([]string, error) {
	u := fmt.Sprintf("%s/query?q=show%%20databases", daemonUrl)
	resp, err := http.Get(u)
	if err != nil {
		return nil, fmt.Errorf("listDatabases error: %s", err.Error())
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	// Do ad-hoc parsing to find existing database names:
	// {"results":[{"series":[{"name":"databases","columns":["name"],"values":[["_internal"],["benchmark_db"]]}]}]}%
	type listingType struct {
		Results []struct {
			Series []struct {
				Values [][]string
			}
		}
	}
	var listing listingType
	err = json.Unmarshal(body, &listing)
	if err != nil {
		return nil, err
	}

	ret := []string{}
	for _, nestedName := range listing.Results[0].Series[0].Values {
		name := nestedName[0]
		// the _internal database is skipped:
		if name == "_internal" {
			continue
		}
		ret = append(ret, name)
	}
	return ret, nil
}
<|endoftext|>


<commit_before>package commands

import (
	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/commands/add"
	"github.com/urfave/cli"
)

func init() {
	Commands = append(Commands, cli.Command{
		Name:      "add",
		Usage:     "add servers, discs, etc - see `bytemark help create <kind of thing> `",
		UsageText: "add server|group|disc|backup",
		Description: `add a new group, server, disc or backup

  add disc[s] [--disc <disc spec>]... <cloud server>
  add group [--account <name>] <name>
  add server (see bytemark help create server)
  add backup <cloud server> <disc label>

A disc spec looks like the following: label:grade:size
The label and grade fields are optional. If grade is empty, defaults to sata.
If there are two fields, they are assumed to be grade and size.
Multiple --disc flags can be used to create multiple discs`,
		Action:      cli.ShowSubcommandHelp,
		Subcommands: add.Commands,
	})
}
<commit_msg>update usage text<commit_after>package commands

import (
	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/commands/add"
	"github.com/urfave/cli"
)

func init() {
	Commands = append(Commands, cli.Command{
		Name:      "add",
		Usage:     "add servers, discs, etc - see `bytemark help add <kind of thing> `",
		UsageText: "add server|group|disc|backup",
		Description: `add a new group, server, disc or backup

  add disc[s] [--disc <disc spec>]... <cloud server>
  add group [--account <name>] <name>
  add server (see bytemark help create server)
  add backup <cloud server> <disc label>

A disc spec looks like the following: label:grade:size
The label and grade fields are optional. If grade is empty, defaults to sata.
If there are two fields, they are assumed to be grade and size.
Multiple --disc flags can be used to create multiple discs`,
		Action:      cli.ShowSubcommandHelp,
		Subcommands: add.Commands,
	})
}
<|endoftext|>


<commit_before>package cmd_teamfolder

import (
	"flag"
	"github.com/watermint/toolbox/app/app_report"
	"github.com/watermint/toolbox/cmd"
	"github.com/watermint/toolbox/domain/infra/api_auth_impl"
	"github.com/watermint/toolbox/domain/usecase/uc_teamfolder_mirror"
)

type CmdTeamFolderMirror struct {
	*cmd.SimpleCommandlet

	optSrcTeamAlias   string
	optDstTeamAlias   string
	optAllTeamFolders bool

	report app_report.Factory
}

func (CmdTeamFolderMirror) Name() string {
	return "mirror"
}

func (CmdTeamFolderMirror) Desc() string {
	return "cmd.teamfolder.mirror.desc"
}

func (CmdTeamFolderMirror) Usage() func(usage cmd.CommandUsage) {
	return nil
}

func (z *CmdTeamFolderMirror) FlagConfig(f *flag.FlagSet) {
	z.report.ExecContext = z.ExecContext
	z.report.FlagConfig(f)

	descFromAccount := z.ExecContext.Msg("cmd.teamfolder.mirror.flag.src_account").T()
	f.StringVar(&z.optSrcTeamAlias, "alias-src", "mirror-src", descFromAccount)

	descToAccount := z.ExecContext.Msg("cmd.teamfolder.mirror.flag.dst_account").T()
	f.StringVar(&z.optDstTeamAlias, "alias-dest", "mirror-dst", descToAccount)

	descAll := z.ExecContext.Msg("cmd.teamfolder.mirror.flag.all").T()
	f.BoolVar(&z.optAllTeamFolders, "all", false, descAll)
}

func (z *CmdTeamFolderMirror) Exec(args []string) {
	if z.optSrcTeamAlias == "" ||
		z.optDstTeamAlias == "" {

		z.ExecContext.Msg("cmd.teamfolder.mirror.err.not_enough_params").TellError()
		return
	}
	if z.optSrcTeamAlias == z.optDstTeamAlias {
		z.ExecContext.Msg("cmd.teamfolder.mirror.err.same_team").TellError()
		return
	}
	if len(args) < 1 && !z.optAllTeamFolders {
		z.ExecContext.Msg("cmd.teamfolder.mirror.err.not_enough_arguments").TellError()
		return
	}
	var err error

	// Ask for SRC account authentication
	z.ExecContext.Msg("cmd.teamfolder.mirror.prompt.ask_src_file_account_auth").WithData(struct {
		Alias string
	}{
		Alias: z.optSrcTeamAlias,
	}).Tell()
	ctxFileSrc, err := api_auth_impl.Auth(z.ExecContext, api_auth_impl.PeerName(z.optSrcTeamAlias), api_auth_impl.BusinessFile())
	if err != nil {
		return
	}

	// Ask for SRC account authentication
	z.ExecContext.Msg("cmd.teamfolder.mirror.prompt.ask_src_mgmt_account_auth").WithData(struct {
		Alias string
	}{
		Alias: z.optSrcTeamAlias,
	}).Tell()
	ctxMgtSrc, err := api_auth_impl.Auth(z.ExecContext, api_auth_impl.PeerName(z.optSrcTeamAlias), api_auth_impl.BusinessManagement())
	if err != nil {
		return
	}

	// Ask for DST account authentication
	z.ExecContext.Msg("cmd.teamfolder.mirror.prompt.ask_dst_file_account_auth").WithData(struct {
		Alias string
	}{
		Alias: z.optDstTeamAlias,
	}).Tell()
	ctxFileDst, err := api_auth_impl.Auth(z.ExecContext, api_auth_impl.PeerName(z.optDstTeamAlias), api_auth_impl.BusinessFile())
	if err != nil {
		return
	}

	// Ask for DST account authentication
	z.ExecContext.Msg("cmd.teamfolder.mirror.prompt.ask_dst_mgmt_account_auth").WithData(struct {
		Alias string
	}{
		Alias: z.optDstTeamAlias,
	}).Tell()
	ctxMgtDst, err := api_auth_impl.Auth(z.ExecContext, api_auth_impl.PeerName(z.optDstTeamAlias), api_auth_impl.BusinessManagement())
	if err != nil {
		return
	}

	ucm := uc_teamfolder_mirror.New(ctxFileSrc, ctxMgtSrc, ctxFileDst, ctxMgtDst)

	if z.optAllTeamFolders {
		uc, err := ucm.AllFolderScope()
		if err != nil {
			ctxFileSrc.ErrorMsg(err).TellError()
			return
		}
		if err = ucm.Mirror(uc); err != nil {
			ctxFileSrc.ErrorMsg(err).TellError()
		}
	} else {
		uc, err := ucm.PartialScope(args)
		if err != nil {
			ctxFileSrc.ErrorMsg(err).TellError()
			return
		}
		if err = ucm.Mirror(uc); err != nil {
			ctxFileSrc.ErrorMsg(err).TellError()
		}
	}
}
<commit_msg>#59 : fix mirror command<commit_after>package cmd_teamfolder

import (
	"flag"
	"github.com/watermint/toolbox/app/app_report"
	"github.com/watermint/toolbox/cmd"
	"github.com/watermint/toolbox/domain/infra/api_auth_impl"
	"github.com/watermint/toolbox/domain/usecase/uc_teamfolder_mirror"
)

type CmdTeamFolderMirror struct {
	*cmd.SimpleCommandlet

	optSrcTeamAlias   string
	optDstTeamAlias   string
	optAllTeamFolders bool

	report app_report.Factory
}

func (CmdTeamFolderMirror) Name() string {
	return "mirror"
}

func (CmdTeamFolderMirror) Desc() string {
	return "cmd.teamfolder.mirror.desc"
}

func (CmdTeamFolderMirror) Usage() func(usage cmd.CommandUsage) {
	return nil
}

func (z *CmdTeamFolderMirror) FlagConfig(f *flag.FlagSet) {
	z.report.ExecContext = z.ExecContext
	z.report.FlagConfig(f)

	descFromAccount := z.ExecContext.Msg("cmd.teamfolder.mirror.flag.src_account").T()
	f.StringVar(&z.optSrcTeamAlias, "alias-src", "mirror-src", descFromAccount)

	descToAccount := z.ExecContext.Msg("cmd.teamfolder.mirror.flag.dst_account").T()
	f.StringVar(&z.optDstTeamAlias, "alias-dest", "mirror-dst", descToAccount)

	descAll := z.ExecContext.Msg("cmd.teamfolder.mirror.flag.all").T()
	f.BoolVar(&z.optAllTeamFolders, "all", false, descAll)
}

func (z *CmdTeamFolderMirror) Exec(args []string) {
	if z.optSrcTeamAlias == "" ||
		z.optDstTeamAlias == "" {

		z.ExecContext.Msg("cmd.teamfolder.mirror.err.not_enough_params").TellError()
		return
	}
	if z.optSrcTeamAlias == z.optDstTeamAlias {
		z.ExecContext.Msg("cmd.teamfolder.mirror.err.same_team").TellError()
		return
	}
	if len(args) < 1 && !z.optAllTeamFolders {
		z.ExecContext.Msg("cmd.teamfolder.mirror.err.not_enough_arguments").TellError()
		return
	}
	var err error

	// Ask for SRC account authentication
	z.ExecContext.Msg("cmd.teamfolder.mirror.prompt.ask_src_file_account_auth").WithData(struct {
		Alias string
	}{
		Alias: z.optSrcTeamAlias,
	}).Tell()
	ctxFileSrc, err := api_auth_impl.Auth(z.ExecContext, api_auth_impl.PeerName(z.optSrcTeamAlias), api_auth_impl.BusinessFile())
	if err != nil {
		return
	}

	// Ask for SRC account authentication
	z.ExecContext.Msg("cmd.teamfolder.mirror.prompt.ask_src_mgmt_account_auth").WithData(struct {
		Alias string
	}{
		Alias: z.optSrcTeamAlias,
	}).Tell()
	ctxMgtSrc, err := api_auth_impl.Auth(z.ExecContext, api_auth_impl.PeerName(z.optSrcTeamAlias), api_auth_impl.BusinessManagement())
	if err != nil {
		return
	}

	// Ask for DST account authentication
	z.ExecContext.Msg("cmd.teamfolder.mirror.prompt.ask_dst_file_account_auth").WithData(struct {
		Alias string
	}{
		Alias: z.optDstTeamAlias,
	}).Tell()
	ctxFileDst, err := api_auth_impl.Auth(z.ExecContext, api_auth_impl.PeerName(z.optDstTeamAlias), api_auth_impl.BusinessFile())
	if err != nil {
		return
	}

	// Ask for DST account authentication
	z.ExecContext.Msg("cmd.teamfolder.mirror.prompt.ask_dst_mgmt_account_auth").WithData(struct {
		Alias string
	}{
		Alias: z.optDstTeamAlias,
	}).Tell()
	ctxMgtDst, err := api_auth_impl.Auth(z.ExecContext, api_auth_impl.PeerName(z.optDstTeamAlias), api_auth_impl.BusinessManagement())
	if err != nil {
		return
	}

	z.report.Init(z.ExecContext)
	defer z.report.Close()

	ucm := uc_teamfolder_mirror.New(ctxFileSrc, ctxMgtSrc, ctxFileDst, ctxMgtDst, z.report)

	if z.optAllTeamFolders {
		uc, err := ucm.AllFolderScope()
		if err != nil {
			ctxFileSrc.ErrorMsg(err).TellError()
			return
		}
		if err = ucm.Mirror(uc); err != nil {
			ctxFileSrc.ErrorMsg(err).TellError()
		}
	} else {
		uc, err := ucm.PartialScope(args)
		if err != nil {
			ctxFileSrc.ErrorMsg(err).TellError()
			return
		}
		if err = ucm.Mirror(uc); err != nil {
			ctxFileSrc.ErrorMsg(err).TellError()
		}
	}
}
<|endoftext|>