<commit_before>package main

import (
	"crypto/tls"
	"crypto/x509"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"os"
	"time"
)

func main() {
	var host, port, caFileName string
	switch len(os.Args) {
	case 4:
		caFileName = os.Args[3]
		fallthrough
	case 3:
		host = os.Args[1]
		port = os.Args[2]
	default:
		fmt.Printf("usage: %v <host> <port> [cafile]\n", os.Args[0])
		os.Exit(1)
	}

	if err := checkTLS(host, port, caFileName); err != nil {
		fmt.Println("FATAL:", err)
		os.Exit(1)
	}
}

func checkTLS(host, port, caFileName string) error {
	client := &http.Client{
		Timeout: 5 * time.Second,
	}

	if caFileName != "" {
		roots, err := loadRootCA(caFileName)
		if err != nil {
			return err
		}
		client.Transport = &http.Transport{
			TLSClientConfig: &tls.Config{RootCAs: roots},
		}
	}

	uri := "https://" + net.JoinHostPort(host, port)
	if _, err := client.Get(uri); err != nil {
		if urlerr, ok := err.(*url.Error); ok {
			if operr, ok := urlerr.Err.(*net.OpError); ok {
				if operr.Op == "dial" {
					// Connection errors are fatal without verdict
					return err
				}
			}
		}
		fmt.Println(err.Error())
		fmt.Println("REJECT")
		return nil
	}
	fmt.Println("ACCEPT")
	return nil
}

func loadRootCA(fileName string) (roots *x509.CertPool, err error) {
	cadata, err := ioutil.ReadFile(fileName)
	if err != nil {
		return
	}
	roots = x509.NewCertPool()
	if ok := roots.AppendCertsFromPEM(cadata); !ok {
		return nil, errors.New("failed to parse CA file")
	}
	return roots, nil
}
<commit_msg>go stub: timeouts are fatal without verdict<commit_after>package main

import (
	"crypto/tls"
	"crypto/x509"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"os"
	"time"
)

func main() {
	var host, port, caFileName string
	switch len(os.Args) {
	case 4:
		caFileName = os.Args[3]
		fallthrough
	case 3:
		host = os.Args[1]
		port = os.Args[2]
	default:
		fmt.Printf("usage: %v <host> <port> [cafile]\n", os.Args[0])
		os.Exit(1)
	}

	if err := checkTLS(host, port, caFileName); err != nil {
		fmt.Println("FATAL:", err)
		os.Exit(1)
	}
}

func checkTLS(host, port, caFileName string) error {
	client := &http.Client{
		Timeout: 5 * time.Second,
	}

	if caFileName != "" {
		roots, err := loadRootCA(caFileName)
		if err != nil {
			return err
		}
		client.Transport = &http.Transport{
			TLSClientConfig: &tls.Config{RootCAs: roots},
		}
	}

	uri := "https://" + net.JoinHostPort(host, port)
	if _, err := client.Get(uri); err != nil {
		// Timeouts are fatal without verdict
		if timeouterr, ok := err.(timeouter); ok {
			if timeouterr.Timeout() {
				return err
			}
		}
		// Connection errors from dialer are fatal without verdict
		if urlerr, ok := err.(*url.Error); ok {
			if operr, ok := urlerr.Err.(*net.OpError); ok {
				if operr.Op == "dial" {
					return err
				}
			}
		}

		fmt.Println(err.Error())
		fmt.Println("REJECT")
		return nil
	}
	fmt.Println("ACCEPT")
	return nil
}

type timeouter interface {
	Timeout() bool
}

func loadRootCA(fileName string) (roots *x509.CertPool, err error) {
	cadata, err := ioutil.ReadFile(fileName)
	if err != nil {
		return
	}
	roots = x509.NewCertPool()
	if ok := roots.AppendCertsFromPEM(cadata); !ok {
		return nil, errors.New("failed to parse CA file")
	}
	return roots, nil
}
<|endoftext|>


<commit_before>package ec2

import (
	"github.com/jagregory/cfval/resources/common"
	. "github.com/jagregory/cfval/schema"
)

// see: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-internet-gateway.html
func InternetGateway() Resource {
	return Resource{
		AwsType: "AWS::EC2::InternetGateway",

		// Name
		ReturnValue: Schema{
			Type: ValueString,
		},

		Properties: Properties{
			"Tags": Schema{
				Type:  common.ResourceTag,
				Array: true,
			},
		},
	}
}
<commit_msg>Change Ref value of Internet Gateway to an ID despite what the docs say<commit_after>package ec2

import (
	"github.com/jagregory/cfval/resources/common"
	. "github.com/jagregory/cfval/schema"
)

// see: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-internet-gateway.html
func InternetGateway() Resource {
	return Resource{
		AwsType: "AWS::EC2::InternetGateway",

		// Name -- not sure about this. Docs say Name, but my testing we can Ref
		//         this into an InternetGatewayId property successfully.
		ReturnValue: Schema{
			Type: InternetGatewayID,
		},

		Properties: Properties{
			"Tags": Schema{
				Type:  common.ResourceTag,
				Array: true,
			},
		},
	}
}
<|endoftext|>


<commit_before><commit_msg>cmbine ext<commit_after><|endoftext|>


<commit_before>/*
Copyright 2014 Google Inc. All rights reserved.

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

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

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

package s2

import (
	"math"

	"github.com/golang/geo/r1"
	"github.com/golang/geo/r2"
	"github.com/golang/geo/s1"
)

// Cell is an S2 region object that represents a cell. Unlike CellIDs,
// it supports efficient containment and intersection tests. However, it is
// also a more expensive representation.
type Cell struct {
	face        int8
	level       int8
	orientation int8
	id          CellID
	uv          r2.Rect
}

// CellFromCellID constructs a Cell corresponding to the given CellID.
func CellFromCellID(id CellID) Cell {
	c := Cell{}
	c.id = id
	f, i, j, o := c.id.faceIJOrientation()
	c.face = int8(f)
	c.level = int8(c.id.Level())
	c.orientation = int8(o)
	c.uv = ijLevelToBoundUV(i, j, int(c.level))
	return c
}

// CellFromPoint constructs a cell for the given Point.
func CellFromPoint(p Point) Cell {
	return CellFromCellID(cellIDFromPoint(p))
}

// CellFromLatLng constructs a cell for the given LatLng.
func CellFromLatLng(ll LatLng) Cell {
	return CellFromCellID(CellIDFromLatLng(ll))
}

// IsLeaf returns whether this Cell is a leaf or not.
func (c Cell) IsLeaf() bool {
	return c.level == maxLevel
}

// SizeIJ returns the CellID value for the cells level.
func (c Cell) SizeIJ() int {
	return sizeIJ(int(c.level))
}

// Vertex returns the k-th vertex of the cell (k = [0,3]) in CCW order
// (lower left, lower right, upper right, upper left in the UV plane).
func (c Cell) Vertex(k int) Point {
	return Point{faceUVToXYZ(int(c.face), c.uv.Vertices()[k].X, c.uv.Vertices()[k].Y).Normalize()}
}

// Edge returns the inward-facing normal of the great circle passing through
// the CCW ordered edge from vertex k to vertex k+1 (mod 4).
func (c Cell) Edge(k int) Point {
	switch k {
	case 0:
		return Point{vNorm(int(c.face), c.uv.Y.Lo).Normalize()} // Bottom
	case 1:
		return Point{uNorm(int(c.face), c.uv.X.Hi).Normalize()} // Right
	case 2:
		return Point{vNorm(int(c.face), c.uv.Y.Hi).Mul(-1.0).Normalize()} // Top
	default:
		return Point{uNorm(int(c.face), c.uv.X.Lo).Mul(-1.0).Normalize()} // Left
	}
}

// BoundUV returns the bounds of this cell in (u,v)-space.
func (c Cell) BoundUV() r2.Rect {
	return c.uv
}

// ExactArea returns the area of this cell as accurately as possible.
func (c Cell) ExactArea() float64 {
	v0, v1, v2, v3 := c.Vertex(0), c.Vertex(1), c.Vertex(2), c.Vertex(3)
	return PointArea(v0, v1, v2) + PointArea(v0, v2, v3)
}

// ApproxArea returns the approximate area of this cell. This method is accurate
// to within 3% percent for all cell sizes and accurate to within 0.1% for cells
// at level 5 or higher (i.e. squares 350km to a side or smaller on the Earth's
// surface). It is moderately cheap to compute.
func (c Cell) ApproxArea() float64 {
	// All cells at the first two levels have the same area.
	if c.level < 2 {
		return c.AverageArea()
	}

	// First, compute the approximate area of the cell when projected
	// perpendicular to its normal. The cross product of its diagonals gives
	// the normal, and the length of the normal is twice the projected area.
	flatArea := 0.5 * (c.Vertex(2).Sub(c.Vertex(0).Vector).
		Cross(c.Vertex(3).Sub(c.Vertex(1).Vector)).Norm())

	// Now, compensate for the curvature of the cell surface by pretending
	// that the cell is shaped like a spherical cap. The ratio of the
	// area of a spherical cap to the area of its projected disc turns out
	// to be 2 / (1 + sqrt(1 - r*r)) where r is the radius of the disc.
	// For example, when r=0 the ratio is 1, and when r=1 the ratio is 2.
	// Here we set Pi*r*r == flatArea to find the equivalent disc.
	return flatArea * 2 / (1 + math.Sqrt(1-math.Min(1/math.Pi*flatArea, 1)))
}

// AverageArea returns the average area of cells at the level of this cell.
// This is accurate to within a factor of 1.7.
func (c Cell) AverageArea() float64 {
	return AvgAreaMetric.Value(int(c.level))
}

// IntersectsCell reports whether the intersection of this cell and the other cell is not nil.
func (c Cell) IntersectsCell(oc Cell) bool {
	return c.id.Intersects(oc.id)
}

// ContainsCell reports whether this cell contains the other cell.
func (c Cell) ContainsCell(oc Cell) bool {
	return c.id.Contains(oc.id)
}

// latitude returns the latitude of the cell vertex given by (i,j), where "i" and "j" are either 0 or 1.
func (c Cell) latitude(i, j int) float64 {
	var u, v float64
	switch {
	case i == 0 && j == 0:
		u = c.uv.X.Lo
		v = c.uv.Y.Lo
	case i == 0 && j == 1:
		u = c.uv.X.Lo
		v = c.uv.Y.Hi
	case i == 1 && j == 0:
		u = c.uv.X.Hi
		v = c.uv.Y.Lo
	case i == 1 && j == 1:
		u = c.uv.X.Hi
		v = c.uv.Y.Hi
	default:
		panic("i and/or j is out of bound")
	}
	return latitude(Point{faceUVToXYZ(int(c.face), u, v)}).Radians()
}

// longitude returns the longitude of the cell vertex given by (i,j), where "i" and "j" are either 0 or 1.
func (c Cell) longitude(i, j int) float64 {
	var u, v float64
	switch {
	case i == 0 && j == 0:
		u = c.uv.X.Lo
		v = c.uv.Y.Lo
	case i == 0 && j == 1:
		u = c.uv.X.Lo
		v = c.uv.Y.Hi
	case i == 1 && j == 0:
		u = c.uv.X.Hi
		v = c.uv.Y.Lo
	case i == 1 && j == 1:
		u = c.uv.X.Hi
		v = c.uv.Y.Hi
	default:
		panic("i and/or j is out of bound")
	}
	return longitude(Point{faceUVToXYZ(int(c.face), u, v)}).Radians()
}

// TODO(akashagrawal): move these package private variables to a more appropriate location.
var (
	dblEpsilon = math.Nextafter(1, 2) - 1
	poleMinLat = math.Asin(math.Sqrt(1.0/3)) - 0.5*dblEpsilon
)

// RectBound returns the bounding rectangle of this cell.
func (c Cell) RectBound() Rect {
	if c.level > 0 {
		// Except for cells at level 0, the latitude and longitude extremes are
		// attained at the vertices.  Furthermore, the latitude range is
		// determined by one pair of diagonally opposite vertices and the
		// longitude range is determined by the other pair.
		//
		// We first determine which corner (i,j) of the cell has the largest
		// absolute latitude.  To maximize latitude, we want to find the point in
		// the cell that has the largest absolute z-coordinate and the smallest
		// absolute x- and y-coordinates.  To do this we look at each coordinate
		// (u and v), and determine whether we want to minimize or maximize that
		// coordinate based on the axis direction and the cell's (u,v) quadrant.
		u := c.uv.X.Lo + c.uv.X.Hi
		v := c.uv.Y.Lo + c.uv.Y.Hi
		var i, j int
		if uAxis(int(c.face)).Z == 0 {
			if u < 0 {
				i = 1
			}
		} else if u > 0 {
			i = 1
		}
		if vAxis(int(c.face)).Z == 0 {
			if v < 0 {
				j = 1
			}
		} else if v > 0 {
			j = 1
		}
		lat := r1.IntervalFromPoint(c.latitude(i, j)).AddPoint(c.latitude(1-i, 1-j))
		lng := s1.EmptyInterval().AddPoint(c.longitude(i, 1-j)).AddPoint(c.longitude(1-i, j))

		// We grow the bounds slightly to make sure that the bounding rectangle
		// contains LatLngFromPoint(P) for any point P inside the loop L defined by the
		// four *normalized* vertices.  Note that normalization of a vector can
		// change its direction by up to 0.5 * dblEpsilon radians, and it is not
		// enough just to add Normalize calls to the code above because the
		// latitude/longitude ranges are not necessarily determined by diagonally
		// opposite vertex pairs after normalization.
		//
		// We would like to bound the amount by which the latitude/longitude of a
		// contained point P can exceed the bounds computed above.  In the case of
		// longitude, the normalization error can change the direction of rounding
		// leading to a maximum difference in longitude of 2 * dblEpsilon.  In
		// the case of latitude, the normalization error can shift the latitude by
		// up to 0.5 * dblEpsilon and the other sources of error can cause the
		// two latitudes to differ by up to another 1.5 * dblEpsilon, which also
		// leads to a maximum difference of 2 * dblEpsilon.
		return Rect{lat, lng}.expanded(LatLng{s1.Angle(2 * dblEpsilon), s1.Angle(2 * dblEpsilon)}).PolarClosure()
	}

	// The 4 cells around the equator extend to +/-45 degrees latitude at the
	// midpoints of their top and bottom edges.  The two cells covering the
	// poles extend down to +/-35.26 degrees at their vertices.  The maximum
	// error in this calculation is 0.5 * dblEpsilon.
	var bound Rect
	switch c.face {
	case 0:
		bound = Rect{r1.Interval{-math.Pi / 4, math.Pi / 4}, s1.Interval{-math.Pi / 4, math.Pi / 4}}
	case 1:
		bound = Rect{r1.Interval{-math.Pi / 4, math.Pi / 4}, s1.Interval{math.Pi / 4, 3 * math.Pi / 4}}
	case 2:
		bound = Rect{r1.Interval{poleMinLat, math.Pi / 2}, s1.FullInterval()}
	case 3:
		bound = Rect{r1.Interval{-math.Pi / 4, math.Pi / 4}, s1.Interval{3 * math.Pi / 4, -3 * math.Pi / 4}}
	case 4:
		bound = Rect{r1.Interval{-math.Pi / 4, math.Pi / 4}, s1.Interval{-3 * math.Pi / 4, -math.Pi / 4}}
	default:
		bound = Rect{r1.Interval{-math.Pi / 2, -poleMinLat}, s1.FullInterval()}
	}

	// Finally, we expand the bound to account for the error when a point P is
	// converted to an LatLng to test for containment. (The bound should be
	// large enough so that it contains the computed LatLng of any contained
	// point, not just the infinite-precision version.) We don't need to expand
	// longitude because longitude is calculated via a single call to math.Atan2,
	// which is guaranteed to be semi-monotonic.
	return bound.expanded(LatLng{s1.Angle(dblEpsilon), s1.Angle(0)})
}

// CapBound returns the bounding cap of this cell.
func (c Cell) CapBound() Cap {
	// We use the cell center in (u,v)-space as the cap axis.  This vector is very close
	// to GetCenter() and faster to compute.  Neither one of these vectors yields the
	// bounding cap with minimal surface area, but they are both pretty close.
	cap := CapFromPoint(Point{faceUVToXYZ(int(c.face), c.uv.Center().X, c.uv.Center().Y).Normalize()})
	for k := 0; k < 4; k++ {
		cap = cap.AddPoint(c.Vertex(k))
	}
	return cap
}

// ContainsPoint reports whether this cell contains the given point. Note that
// unlike Loop/Polygon, a Cell is considered to be a closed set. This means
// that a point on a Cell's edge or vertex belong to the Cell and the relevant
// adjacent Cells too.
//
// If you want every point to be contained by exactly one Cell,
// you will need to convert the Cell to a Loop.
func (c Cell) ContainsPoint(p Point) bool {
	var uv r2.Point
	var ok bool
	if uv.X, uv.Y, ok = faceXYZToUV(int(c.face), p); !ok {
		return false
	}

	// Expand the (u,v) bound to ensure that
	//
	//   CellFromPoint(p).ContainsPoint(p)
	//
	// is always true. To do this, we need to account for the error when
	// converting from (u,v) coordinates to (s,t) coordinates. In the
	// normal case the total error is at most dblEpsilon.
	return c.uv.ExpandedByMargin(dblEpsilon).ContainsPoint(uv)
}

// TODO(roberts, or $SOMEONE): Differences from C++, almost everything else still.
// Implement the accessor methods on the internal fields.
<commit_msg>s2: Add Cell.Center.<commit_after>/*
Copyright 2014 Google Inc. All rights reserved.

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

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

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

package s2

import (
	"math"

	"github.com/golang/geo/r1"
	"github.com/golang/geo/r2"
	"github.com/golang/geo/s1"
)

// Cell is an S2 region object that represents a cell. Unlike CellIDs,
// it supports efficient containment and intersection tests. However, it is
// also a more expensive representation.
type Cell struct {
	face        int8
	level       int8
	orientation int8
	id          CellID
	uv          r2.Rect
}

// CellFromCellID constructs a Cell corresponding to the given CellID.
func CellFromCellID(id CellID) Cell {
	c := Cell{}
	c.id = id
	f, i, j, o := c.id.faceIJOrientation()
	c.face = int8(f)
	c.level = int8(c.id.Level())
	c.orientation = int8(o)
	c.uv = ijLevelToBoundUV(i, j, int(c.level))
	return c
}

// CellFromPoint constructs a cell for the given Point.
func CellFromPoint(p Point) Cell {
	return CellFromCellID(cellIDFromPoint(p))
}

// CellFromLatLng constructs a cell for the given LatLng.
func CellFromLatLng(ll LatLng) Cell {
	return CellFromCellID(CellIDFromLatLng(ll))
}

// IsLeaf returns whether this Cell is a leaf or not.
func (c Cell) IsLeaf() bool {
	return c.level == maxLevel
}

// SizeIJ returns the CellID value for the cells level.
func (c Cell) SizeIJ() int {
	return sizeIJ(int(c.level))
}

// Vertex returns the k-th vertex of the cell (k = [0,3]) in CCW order
// (lower left, lower right, upper right, upper left in the UV plane).
func (c Cell) Vertex(k int) Point {
	return Point{faceUVToXYZ(int(c.face), c.uv.Vertices()[k].X, c.uv.Vertices()[k].Y).Normalize()}
}

// Edge returns the inward-facing normal of the great circle passing through
// the CCW ordered edge from vertex k to vertex k+1 (mod 4).
func (c Cell) Edge(k int) Point {
	switch k {
	case 0:
		return Point{vNorm(int(c.face), c.uv.Y.Lo).Normalize()} // Bottom
	case 1:
		return Point{uNorm(int(c.face), c.uv.X.Hi).Normalize()} // Right
	case 2:
		return Point{vNorm(int(c.face), c.uv.Y.Hi).Mul(-1.0).Normalize()} // Top
	default:
		return Point{uNorm(int(c.face), c.uv.X.Lo).Mul(-1.0).Normalize()} // Left
	}
}

// BoundUV returns the bounds of this cell in (u,v)-space.
func (c Cell) BoundUV() r2.Rect {
	return c.uv
}

// Center returns the direction vector corresponding to the center in
// (s,t)-space of the given cell. This is the point at which the cell is
// divided into four subcells; it is not necessarily the centroid of the
// cell in (u,v)-space or (x,y,z)-space
func (c Cell) Center() Point {
	return Point{c.id.rawPoint().Normalize()}
}

// ExactArea returns the area of this cell as accurately as possible.
func (c Cell) ExactArea() float64 {
	v0, v1, v2, v3 := c.Vertex(0), c.Vertex(1), c.Vertex(2), c.Vertex(3)
	return PointArea(v0, v1, v2) + PointArea(v0, v2, v3)
}

// ApproxArea returns the approximate area of this cell. This method is accurate
// to within 3% percent for all cell sizes and accurate to within 0.1% for cells
// at level 5 or higher (i.e. squares 350km to a side or smaller on the Earth's
// surface). It is moderately cheap to compute.
func (c Cell) ApproxArea() float64 {
	// All cells at the first two levels have the same area.
	if c.level < 2 {
		return c.AverageArea()
	}

	// First, compute the approximate area of the cell when projected
	// perpendicular to its normal. The cross product of its diagonals gives
	// the normal, and the length of the normal is twice the projected area.
	flatArea := 0.5 * (c.Vertex(2).Sub(c.Vertex(0).Vector).
		Cross(c.Vertex(3).Sub(c.Vertex(1).Vector)).Norm())

	// Now, compensate for the curvature of the cell surface by pretending
	// that the cell is shaped like a spherical cap. The ratio of the
	// area of a spherical cap to the area of its projected disc turns out
	// to be 2 / (1 + sqrt(1 - r*r)) where r is the radius of the disc.
	// For example, when r=0 the ratio is 1, and when r=1 the ratio is 2.
	// Here we set Pi*r*r == flatArea to find the equivalent disc.
	return flatArea * 2 / (1 + math.Sqrt(1-math.Min(1/math.Pi*flatArea, 1)))
}

// AverageArea returns the average area of cells at the level of this cell.
// This is accurate to within a factor of 1.7.
func (c Cell) AverageArea() float64 {
	return AvgAreaMetric.Value(int(c.level))
}

// IntersectsCell reports whether the intersection of this cell and the other cell is not nil.
func (c Cell) IntersectsCell(oc Cell) bool {
	return c.id.Intersects(oc.id)
}

// ContainsCell reports whether this cell contains the other cell.
func (c Cell) ContainsCell(oc Cell) bool {
	return c.id.Contains(oc.id)
}

// latitude returns the latitude of the cell vertex given by (i,j), where "i" and "j" are either 0 or 1.
func (c Cell) latitude(i, j int) float64 {
	var u, v float64
	switch {
	case i == 0 && j == 0:
		u = c.uv.X.Lo
		v = c.uv.Y.Lo
	case i == 0 && j == 1:
		u = c.uv.X.Lo
		v = c.uv.Y.Hi
	case i == 1 && j == 0:
		u = c.uv.X.Hi
		v = c.uv.Y.Lo
	case i == 1 && j == 1:
		u = c.uv.X.Hi
		v = c.uv.Y.Hi
	default:
		panic("i and/or j is out of bound")
	}
	return latitude(Point{faceUVToXYZ(int(c.face), u, v)}).Radians()
}

// longitude returns the longitude of the cell vertex given by (i,j), where "i" and "j" are either 0 or 1.
func (c Cell) longitude(i, j int) float64 {
	var u, v float64
	switch {
	case i == 0 && j == 0:
		u = c.uv.X.Lo
		v = c.uv.Y.Lo
	case i == 0 && j == 1:
		u = c.uv.X.Lo
		v = c.uv.Y.Hi
	case i == 1 && j == 0:
		u = c.uv.X.Hi
		v = c.uv.Y.Lo
	case i == 1 && j == 1:
		u = c.uv.X.Hi
		v = c.uv.Y.Hi
	default:
		panic("i and/or j is out of bound")
	}
	return longitude(Point{faceUVToXYZ(int(c.face), u, v)}).Radians()
}

// TODO(akashagrawal): move these package private variables to a more appropriate location.
var (
	dblEpsilon = math.Nextafter(1, 2) - 1
	poleMinLat = math.Asin(math.Sqrt(1.0/3)) - 0.5*dblEpsilon
)

// RectBound returns the bounding rectangle of this cell.
func (c Cell) RectBound() Rect {
	if c.level > 0 {
		// Except for cells at level 0, the latitude and longitude extremes are
		// attained at the vertices.  Furthermore, the latitude range is
		// determined by one pair of diagonally opposite vertices and the
		// longitude range is determined by the other pair.
		//
		// We first determine which corner (i,j) of the cell has the largest
		// absolute latitude.  To maximize latitude, we want to find the point in
		// the cell that has the largest absolute z-coordinate and the smallest
		// absolute x- and y-coordinates.  To do this we look at each coordinate
		// (u and v), and determine whether we want to minimize or maximize that
		// coordinate based on the axis direction and the cell's (u,v) quadrant.
		u := c.uv.X.Lo + c.uv.X.Hi
		v := c.uv.Y.Lo + c.uv.Y.Hi
		var i, j int
		if uAxis(int(c.face)).Z == 0 {
			if u < 0 {
				i = 1
			}
		} else if u > 0 {
			i = 1
		}
		if vAxis(int(c.face)).Z == 0 {
			if v < 0 {
				j = 1
			}
		} else if v > 0 {
			j = 1
		}
		lat := r1.IntervalFromPoint(c.latitude(i, j)).AddPoint(c.latitude(1-i, 1-j))
		lng := s1.EmptyInterval().AddPoint(c.longitude(i, 1-j)).AddPoint(c.longitude(1-i, j))

		// We grow the bounds slightly to make sure that the bounding rectangle
		// contains LatLngFromPoint(P) for any point P inside the loop L defined by the
		// four *normalized* vertices.  Note that normalization of a vector can
		// change its direction by up to 0.5 * dblEpsilon radians, and it is not
		// enough just to add Normalize calls to the code above because the
		// latitude/longitude ranges are not necessarily determined by diagonally
		// opposite vertex pairs after normalization.
		//
		// We would like to bound the amount by which the latitude/longitude of a
		// contained point P can exceed the bounds computed above.  In the case of
		// longitude, the normalization error can change the direction of rounding
		// leading to a maximum difference in longitude of 2 * dblEpsilon.  In
		// the case of latitude, the normalization error can shift the latitude by
		// up to 0.5 * dblEpsilon and the other sources of error can cause the
		// two latitudes to differ by up to another 1.5 * dblEpsilon, which also
		// leads to a maximum difference of 2 * dblEpsilon.
		return Rect{lat, lng}.expanded(LatLng{s1.Angle(2 * dblEpsilon), s1.Angle(2 * dblEpsilon)}).PolarClosure()
	}

	// The 4 cells around the equator extend to +/-45 degrees latitude at the
	// midpoints of their top and bottom edges.  The two cells covering the
	// poles extend down to +/-35.26 degrees at their vertices.  The maximum
	// error in this calculation is 0.5 * dblEpsilon.
	var bound Rect
	switch c.face {
	case 0:
		bound = Rect{r1.Interval{-math.Pi / 4, math.Pi / 4}, s1.Interval{-math.Pi / 4, math.Pi / 4}}
	case 1:
		bound = Rect{r1.Interval{-math.Pi / 4, math.Pi / 4}, s1.Interval{math.Pi / 4, 3 * math.Pi / 4}}
	case 2:
		bound = Rect{r1.Interval{poleMinLat, math.Pi / 2}, s1.FullInterval()}
	case 3:
		bound = Rect{r1.Interval{-math.Pi / 4, math.Pi / 4}, s1.Interval{3 * math.Pi / 4, -3 * math.Pi / 4}}
	case 4:
		bound = Rect{r1.Interval{-math.Pi / 4, math.Pi / 4}, s1.Interval{-3 * math.Pi / 4, -math.Pi / 4}}
	default:
		bound = Rect{r1.Interval{-math.Pi / 2, -poleMinLat}, s1.FullInterval()}
	}

	// Finally, we expand the bound to account for the error when a point P is
	// converted to an LatLng to test for containment. (The bound should be
	// large enough so that it contains the computed LatLng of any contained
	// point, not just the infinite-precision version.) We don't need to expand
	// longitude because longitude is calculated via a single call to math.Atan2,
	// which is guaranteed to be semi-monotonic.
	return bound.expanded(LatLng{s1.Angle(dblEpsilon), s1.Angle(0)})
}

// CapBound returns the bounding cap of this cell.
func (c Cell) CapBound() Cap {
	// We use the cell center in (u,v)-space as the cap axis.  This vector is very close
	// to GetCenter() and faster to compute.  Neither one of these vectors yields the
	// bounding cap with minimal surface area, but they are both pretty close.
	cap := CapFromPoint(Point{faceUVToXYZ(int(c.face), c.uv.Center().X, c.uv.Center().Y).Normalize()})
	for k := 0; k < 4; k++ {
		cap = cap.AddPoint(c.Vertex(k))
	}
	return cap
}

// ContainsPoint reports whether this cell contains the given point. Note that
// unlike Loop/Polygon, a Cell is considered to be a closed set. This means
// that a point on a Cell's edge or vertex belong to the Cell and the relevant
// adjacent Cells too.
//
// If you want every point to be contained by exactly one Cell,
// you will need to convert the Cell to a Loop.
func (c Cell) ContainsPoint(p Point) bool {
	var uv r2.Point
	var ok bool
	if uv.X, uv.Y, ok = faceXYZToUV(int(c.face), p); !ok {
		return false
	}

	// Expand the (u,v) bound to ensure that
	//
	//   CellFromPoint(p).ContainsPoint(p)
	//
	// is always true. To do this, we need to account for the error when
	// converting from (u,v) coordinates to (s,t) coordinates. In the
	// normal case the total error is at most dblEpsilon.
	return c.uv.ExpandedByMargin(dblEpsilon).ContainsPoint(uv)
}

// BUG(roberts): Differences from C++:
// Accessor methods
// Subdivide
// BoundUV
// Distance/DistanceToEdge
// VertexChordDistance
<|endoftext|>


<commit_before>// +build linux,cgo

package devmapper

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

type directLVMConfig struct {
	Device              string
	ThinpPercent        uint64
	ThinpMetaPercent    uint64
	AutoExtendPercent   uint64
	AutoExtendThreshold uint64
}

var (
	errThinpPercentMissing = errors.New("must set both `dm.thinp_percent` and `dm.thinp_metapercent` if either is specified")
	errThinpPercentTooBig  = errors.New("combined `dm.thinp_percent` and `dm.thinp_metapercent` must not be greater than 100")
	errMissingSetupDevice  = errors.New("must provide device path in `dm.directlvm_device` in order to configure direct-lvm")
)

func validateLVMConfig(cfg directLVMConfig) error {
	if cfg.Device == "" {
		return errMissingSetupDevice
	}
	if (cfg.ThinpPercent > 0 && cfg.ThinpMetaPercent == 0) || cfg.ThinpMetaPercent > 0 && cfg.ThinpPercent == 0 {
		return errThinpPercentMissing
	}

	if cfg.ThinpPercent+cfg.ThinpMetaPercent > 100 {
		return errThinpPercentTooBig
	}
	return nil
}

func checkDevAvailable(dev string) error {
	lvmScan, err := exec.LookPath("lvmdiskscan")
	if err != nil {
		logrus.Debug("could not find lvmdiskscan")
		return nil
	}

	out, err := exec.Command(lvmScan).CombinedOutput()
	if err != nil {
		logrus.WithError(err).Error(string(out))
		return nil
	}

	if !bytes.Contains(out, []byte(dev)) {
		return errors.Errorf("%s is not available for use with devicemapper", dev)
	}
	return nil
}

func checkDevInVG(dev string) error {
	pvDisplay, err := exec.LookPath("pvdisplay")
	if err != nil {
		logrus.Debug("could not find pvdisplay")
		return nil
	}

	out, err := exec.Command(pvDisplay, dev).CombinedOutput()
	if err != nil {
		logrus.WithError(err).Error(string(out))
		return nil
	}

	scanner := bufio.NewScanner(bytes.NewReader(bytes.TrimSpace(out)))
	for scanner.Scan() {
		fields := strings.SplitAfter(strings.TrimSpace(scanner.Text()), "VG Name")
		if len(fields) > 1 {
			// got "VG Name" line"
			vg := strings.TrimSpace(fields[1])
			if len(vg) > 0 {
				return errors.Errorf("%s is already part of a volume group %q: must remove this device from any volume group or provide a different device", dev, vg)
			}
			logrus.Error(fields)
			break
		}
	}
	return nil
}

func checkDevHasFS(dev string) error {
	blkid, err := exec.LookPath("blkid")
	if err != nil {
		logrus.Debug("could not find blkid")
		return nil
	}

	out, err := exec.Command(blkid, dev).CombinedOutput()
	if err != nil {
		logrus.WithError(err).Error(string(out))
		return nil
	}

	fields := bytes.Fields(out)
	for _, f := range fields {
		kv := bytes.Split(f, []byte{'='})
		if bytes.Equal(kv[0], []byte("TYPE")) {
			v := bytes.Trim(kv[1], "\"")
			if len(v) > 0 {
				return errors.Errorf("%s has a filesystem already, use dm.directlvm_device_force=true if you want to wipe the device", dev)
			}
			return nil
		}
	}
	return nil
}

func verifyBlockDevice(dev string, force bool) error {
	realPath, err := filepath.Abs(dev)
	if err != nil {
		return errors.Errorf("unable to get absolute path for %s: %s", dev, err)
	}
	if realPath, err = filepath.EvalSymlinks(realPath); err != nil {
		return errors.Errorf("failed to canonicalise path for %s: %s", dev, err)
	}
	if err := checkDevAvailable(realPath); err != nil {
		return err
	}
	if err := checkDevInVG(realPath); err != nil {
		return err
	}

	if force {
		return nil
	}

	if err := checkDevHasFS(realPath); err != nil {
		return err
	}
	return nil
}

func readLVMConfig(root string) (directLVMConfig, error) {
	var cfg directLVMConfig

	p := filepath.Join(root, "setup-config.json")
	b, err := ioutil.ReadFile(p)
	if err != nil {
		if os.IsNotExist(err) {
			return cfg, nil
		}
		return cfg, errors.Wrap(err, "error reading existing setup config")
	}

	// check if this is just an empty file, no need to produce a json error later if so
	if len(b) == 0 {
		return cfg, nil
	}

	err = json.Unmarshal(b, &cfg)
	return cfg, errors.Wrap(err, "error unmarshaling previous device setup config")
}

func writeLVMConfig(root string, cfg directLVMConfig) error {
	p := filepath.Join(root, "setup-config.json")
	b, err := json.Marshal(cfg)
	if err != nil {
		return errors.Wrap(err, "error marshalling direct lvm config")
	}
	err = ioutil.WriteFile(p, b, 0600)
	return errors.Wrap(err, "error writing direct lvm config to file")
}

func setupDirectLVM(cfg directLVMConfig) error {
	lvmProfileDir := "/etc/lvm/profile"
	binaries := []string{"pvcreate", "vgcreate", "lvcreate", "lvconvert", "lvchange", "thin_check"}

	for _, bin := range binaries {
		if _, err := exec.LookPath(bin); err != nil {
			return errors.Wrap(err, "error looking up command `"+bin+"` while setting up direct lvm")
		}
	}

	err := os.MkdirAll(lvmProfileDir, 0755)
	if err != nil {
		return errors.Wrap(err, "error creating lvm profile directory")
	}

	if cfg.AutoExtendPercent == 0 {
		cfg.AutoExtendPercent = 20
	}

	if cfg.AutoExtendThreshold == 0 {
		cfg.AutoExtendThreshold = 80
	}

	if cfg.ThinpPercent == 0 {
		cfg.ThinpPercent = 95
	}
	if cfg.ThinpMetaPercent == 0 {
		cfg.ThinpMetaPercent = 1
	}

	out, err := exec.Command("pvcreate", "-f", cfg.Device).CombinedOutput()
	if err != nil {
		return errors.Wrap(err, string(out))
	}

	out, err = exec.Command("vgcreate", "storage", cfg.Device).CombinedOutput()
	if err != nil {
		return errors.Wrap(err, string(out))
	}

	out, err = exec.Command("lvcreate", "--wipesignatures", "y", "-n", "thinpool", "storage", "--extents", fmt.Sprintf("%d%%VG", cfg.ThinpPercent)).CombinedOutput()
	if err != nil {
		return errors.Wrap(err, string(out))
	}
	out, err = exec.Command("lvcreate", "--wipesignatures", "y", "-n", "thinpoolmeta", "storage", "--extents", fmt.Sprintf("%d%%VG", cfg.ThinpMetaPercent)).CombinedOutput()
	if err != nil {
		return errors.Wrap(err, string(out))
	}

	out, err = exec.Command("lvconvert", "-y", "--zero", "n", "-c", "512K", "--thinpool", "storage/thinpool", "--poolmetadata", "storage/thinpoolmeta").CombinedOutput()
	if err != nil {
		return errors.Wrap(err, string(out))
	}

	profile := fmt.Sprintf("activation{\nthin_pool_autoextend_threshold=%d\nthin_pool_autoextend_percent=%d\n}", cfg.AutoExtendThreshold, cfg.AutoExtendPercent)
	err = ioutil.WriteFile(lvmProfileDir+"/storage-thinpool.profile", []byte(profile), 0600)
	if err != nil {
		return errors.Wrap(err, "error writing storage thinp autoextend profile")
	}

	out, err = exec.Command("lvchange", "--metadataprofile", "storage-thinpool", "storage/thinpool").CombinedOutput()
	return errors.Wrap(err, string(out))
}
<commit_msg>devmapper: allow devmapper devices as directlvm device<commit_after>// +build linux,cgo

package devmapper

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

type directLVMConfig struct {
	Device              string
	ThinpPercent        uint64
	ThinpMetaPercent    uint64
	AutoExtendPercent   uint64
	AutoExtendThreshold uint64
}

var (
	errThinpPercentMissing = errors.New("must set both `dm.thinp_percent` and `dm.thinp_metapercent` if either is specified")
	errThinpPercentTooBig  = errors.New("combined `dm.thinp_percent` and `dm.thinp_metapercent` must not be greater than 100")
	errMissingSetupDevice  = errors.New("must provide device path in `dm.directlvm_device` in order to configure direct-lvm")
)

func validateLVMConfig(cfg directLVMConfig) error {
	if cfg.Device == "" {
		return errMissingSetupDevice
	}
	if (cfg.ThinpPercent > 0 && cfg.ThinpMetaPercent == 0) || cfg.ThinpMetaPercent > 0 && cfg.ThinpPercent == 0 {
		return errThinpPercentMissing
	}

	if cfg.ThinpPercent+cfg.ThinpMetaPercent > 100 {
		return errThinpPercentTooBig
	}
	return nil
}

func checkDevAvailable(dev string) error {
	lvmScan, err := exec.LookPath("lvmdiskscan")
	if err != nil {
		logrus.Debug("could not find lvmdiskscan")
		return nil
	}

	out, err := exec.Command(lvmScan).CombinedOutput()
	if err != nil {
		logrus.WithError(err).Error(string(out))
		return nil
	}

	if !bytes.Contains(out, []byte(dev)) {
		return errors.Errorf("%s is not available for use with devicemapper", dev)
	}
	return nil
}

func checkDevInVG(dev string) error {
	pvDisplay, err := exec.LookPath("pvdisplay")
	if err != nil {
		logrus.Debug("could not find pvdisplay")
		return nil
	}

	out, err := exec.Command(pvDisplay, dev).CombinedOutput()
	if err != nil {
		logrus.WithError(err).Error(string(out))
		return nil
	}

	scanner := bufio.NewScanner(bytes.NewReader(bytes.TrimSpace(out)))
	for scanner.Scan() {
		fields := strings.SplitAfter(strings.TrimSpace(scanner.Text()), "VG Name")
		if len(fields) > 1 {
			// got "VG Name" line"
			vg := strings.TrimSpace(fields[1])
			if len(vg) > 0 {
				return errors.Errorf("%s is already part of a volume group %q: must remove this device from any volume group or provide a different device", dev, vg)
			}
			logrus.Error(fields)
			break
		}
	}
	return nil
}

func checkDevHasFS(dev string) error {
	blkid, err := exec.LookPath("blkid")
	if err != nil {
		logrus.Debug("could not find blkid")
		return nil
	}

	out, err := exec.Command(blkid, dev).CombinedOutput()
	if err != nil {
		logrus.WithError(err).Error(string(out))
		return nil
	}

	fields := bytes.Fields(out)
	for _, f := range fields {
		kv := bytes.Split(f, []byte{'='})
		if bytes.Equal(kv[0], []byte("TYPE")) {
			v := bytes.Trim(kv[1], "\"")
			if len(v) > 0 {
				return errors.Errorf("%s has a filesystem already, use dm.directlvm_device_force=true if you want to wipe the device", dev)
			}
			return nil
		}
	}
	return nil
}

func verifyBlockDevice(dev string, force bool) error {
	absPath, err := filepath.Abs(dev)
	if err != nil {
		return errors.Errorf("unable to get absolute path for %s: %s", dev, err)
	}
	realPath, err := filepath.EvalSymlinks(absPath)
	if err != nil {
		return errors.Errorf("failed to canonicalise path for %s: %s", dev, err)
	}
	if err := checkDevAvailable(absPath); err != nil {
		logrus.Infof("block device '%s' not available, checking '%s'", absPath, realPath)
		if err := checkDevAvailable(realPath); err != nil {
			return errors.Errorf("neither '%s' nor '%s' are in the output of lvmdiskscan, can't use device.", absPath, realPath)
		}
	}
	if err := checkDevInVG(realPath); err != nil {
		return err
	}

	if force {
		return nil
	}

	if err := checkDevHasFS(realPath); err != nil {
		return err
	}
	return nil
}

func readLVMConfig(root string) (directLVMConfig, error) {
	var cfg directLVMConfig

	p := filepath.Join(root, "setup-config.json")
	b, err := ioutil.ReadFile(p)
	if err != nil {
		if os.IsNotExist(err) {
			return cfg, nil
		}
		return cfg, errors.Wrap(err, "error reading existing setup config")
	}

	// check if this is just an empty file, no need to produce a json error later if so
	if len(b) == 0 {
		return cfg, nil
	}

	err = json.Unmarshal(b, &cfg)
	return cfg, errors.Wrap(err, "error unmarshaling previous device setup config")
}

func writeLVMConfig(root string, cfg directLVMConfig) error {
	p := filepath.Join(root, "setup-config.json")
	b, err := json.Marshal(cfg)
	if err != nil {
		return errors.Wrap(err, "error marshalling direct lvm config")
	}
	err = ioutil.WriteFile(p, b, 0600)
	return errors.Wrap(err, "error writing direct lvm config to file")
}

func setupDirectLVM(cfg directLVMConfig) error {
	lvmProfileDir := "/etc/lvm/profile"
	binaries := []string{"pvcreate", "vgcreate", "lvcreate", "lvconvert", "lvchange", "thin_check"}

	for _, bin := range binaries {
		if _, err := exec.LookPath(bin); err != nil {
			return errors.Wrap(err, "error looking up command `"+bin+"` while setting up direct lvm")
		}
	}

	err := os.MkdirAll(lvmProfileDir, 0755)
	if err != nil {
		return errors.Wrap(err, "error creating lvm profile directory")
	}

	if cfg.AutoExtendPercent == 0 {
		cfg.AutoExtendPercent = 20
	}

	if cfg.AutoExtendThreshold == 0 {
		cfg.AutoExtendThreshold = 80
	}

	if cfg.ThinpPercent == 0 {
		cfg.ThinpPercent = 95
	}
	if cfg.ThinpMetaPercent == 0 {
		cfg.ThinpMetaPercent = 1
	}

	out, err := exec.Command("pvcreate", "-f", cfg.Device).CombinedOutput()
	if err != nil {
		return errors.Wrap(err, string(out))
	}

	out, err = exec.Command("vgcreate", "storage", cfg.Device).CombinedOutput()
	if err != nil {
		return errors.Wrap(err, string(out))
	}

	out, err = exec.Command("lvcreate", "--wipesignatures", "y", "-n", "thinpool", "storage", "--extents", fmt.Sprintf("%d%%VG", cfg.ThinpPercent)).CombinedOutput()
	if err != nil {
		return errors.Wrap(err, string(out))
	}
	out, err = exec.Command("lvcreate", "--wipesignatures", "y", "-n", "thinpoolmeta", "storage", "--extents", fmt.Sprintf("%d%%VG", cfg.ThinpMetaPercent)).CombinedOutput()
	if err != nil {
		return errors.Wrap(err, string(out))
	}

	out, err = exec.Command("lvconvert", "-y", "--zero", "n", "-c", "512K", "--thinpool", "storage/thinpool", "--poolmetadata", "storage/thinpoolmeta").CombinedOutput()
	if err != nil {
		return errors.Wrap(err, string(out))
	}

	profile := fmt.Sprintf("activation{\nthin_pool_autoextend_threshold=%d\nthin_pool_autoextend_percent=%d\n}", cfg.AutoExtendThreshold, cfg.AutoExtendPercent)
	err = ioutil.WriteFile(lvmProfileDir+"/storage-thinpool.profile", []byte(profile), 0600)
	if err != nil {
		return errors.Wrap(err, "error writing storage thinp autoextend profile")
	}

	out, err = exec.Command("lvchange", "--metadataprofile", "storage-thinpool", "storage/thinpool").CombinedOutput()
	return errors.Wrap(err, string(out))
}
<|endoftext|>


<commit_before>package daemon

import (
	"github.com/BluePecker/JwtAuth/dialog/server/parameter/token/request"
	coderQ "github.com/BluePecker/JwtAuth/dialog/server/parameter/coder/request"
	"github.com/BluePecker/JwtAuth/dialog/server/parameter/token/response"
	"github.com/BluePecker/JwtAuth/daemon/coder"
)

func (d *Daemon) List(req request.List) ([]response.Token, error) {
	if sings, err := (*d.Cache).LRange(req.Unique, 0, coder.LoginNum); err != nil {
		return nil, err
	} else {
		ttl := (*d.Cache).TTL(req.Unique)
		tokens := []response.Token{}
		for _, singed := range sings {
			if token, err := coder.Decode(coderQ.Decode{
				JsonWebToken: singed,
			}, (*d.Options).Secret); err == nil && token != nil {
				if claims, ok := token.Claims.(*coder.CustomClaim); ok {
					tokens = append(tokens, response.Token{
						Singed: singed,
						TTL:    ttl,
						Addr:   claims.Addr,
						Device: claims.Device,
					})
				}
			}
		}
		return tokens, nil
	}
}
<commit_msg>add log<commit_after>package daemon

import (
	"github.com/BluePecker/JwtAuth/dialog/server/parameter/token/request"
	coderQ "github.com/BluePecker/JwtAuth/dialog/server/parameter/coder/request"
	"github.com/BluePecker/JwtAuth/dialog/server/parameter/token/response"
	"github.com/BluePecker/JwtAuth/daemon/coder"
	"github.com/Sirupsen/logrus"
)

func (d *Daemon) List(req request.List) ([]response.Token, error) {
	if sings, err := (*d.Cache).LRange(req.Unique, 0, coder.LoginNum); err != nil {
		return nil, err
	} else {
		ttl := (*d.Cache).TTL(req.Unique)
		tokens := []response.Token{}
		for _, singed := range sings {
			if token, err := coder.Decode(coderQ.Decode{
				JsonWebToken: singed,
			}, (*d.Options).Secret); err == nil {
				if claims, ok := token.Claims.(*coder.CustomClaim); ok {
					tokens = append(tokens, response.Token{
						Singed: singed,
						TTL:    ttl,
						Addr:   claims.Addr,
						Device: claims.Device,
					})
				}
			} else {
				logrus.Error(err)
			}
		}
		return tokens, nil
	}
}
<|endoftext|>


<commit_before>package l10n_test

import (
	"testing"

	"github.com/jinzhu/gorm"
	_ "github.com/mattn/go-sqlite3"
	"github.com/qor/qor/l10n"

	_ "github.com/go-sql-driver/mysql"
)

func checkHasErr(t *testing.T, err error) {
	if err != nil {
		t.Error(err)
	}
}

func checkHasProductInLocale(db *gorm.DB, locale string, t *testing.T) {
	var count int
	if db.Where("language_code = ?", locale).Count(&count); count != 1 {
		t.Errorf("should has only one product for locale %v, but found %v", locale, count)
	}
}

func checkHasProductInAllLocales(db *gorm.DB, t *testing.T) {
	checkHasProductInLocale(db, l10n.Global, t)
	checkHasProductInLocale(db, "zh", t)
	checkHasProductInLocale(db, "en", t)
}

func TestCreateWithCreate(t *testing.T) {
	product := Product{Code: "CreateWithCreate"}
	checkHasErr(t, dbGlobal.Create(&product).Error)
	checkHasErr(t, dbCN.Create(&product).Error)
	checkHasErr(t, dbEN.Create(&product).Error)

	checkHasProductInAllLocales(dbGlobal.Model(&Product{}).Where("id = ? AND code = ?", product.ID, "CreateWithCreate"), t)
}

func TestCreateWithSave(t *testing.T) {
	product := Product{Code: "CreateWithSave"}
	checkHasErr(t, dbGlobal.Create(&product).Error)
	checkHasErr(t, dbCN.Create(&product).Error)
	checkHasErr(t, dbEN.Create(&product).Error)

	checkHasProductInAllLocales(dbGlobal.Model(&Product{}).Where("id = ? AND code = ?", product.ID, "CreateWithSave"), t)
}

func TestUpdate(t *testing.T) {
	product := Product{Code: "Update", Name: "global"}
	checkHasErr(t, dbGlobal.Create(&product).Error)
	sharedDB := dbGlobal.Model(&Product{}).Where("id = ? AND code = ?", product.ID, "Update")

	product.Name = "中文名"
	checkHasErr(t, dbCN.Create(&product).Error)
	checkHasProductInLocale(sharedDB.Where("name = ?", "中文名"), "zh", t)

	product.Name = "English Name"
	checkHasErr(t, dbEN.Create(&product).Error)
	checkHasProductInLocale(sharedDB.Where("name = ?", "English Name"), "en", t)

	product.Name = "新的中文名"
	product.Code = "NewCode // should be ignored when update"
	dbCN.Save(&product)
	checkHasProductInLocale(sharedDB.Where("name = ?", "新的中文名"), "zh", t)

	product.Name = "New English Name"
	product.Code = "NewCode // should be ignored when update"
	dbEN.Save(&product)
	checkHasProductInLocale(sharedDB.Where("name = ?", "New English Name"), "en", t)
}

func TestQuery(t *testing.T) {
	product := Product{Code: "Query", Name: "global"}
	dbGlobal.Create(&product)
	dbCN.Create(&product)

	var productCN Product
	dbCN.First(&productCN, product.ID)
	if productCN.LanguageCode != "zh" {
		t.Error("Should find localized zh product with mixed mode")
	}

	if dbCN.Set("l10n:mode", "locale").First(&productCN, product.ID).RecordNotFound() {
		t.Error("Should find localized zh product with locale mode")
	}

	if dbCN.Set("l10n:mode", "global").First(&productCN); productCN.LanguageCode != l10n.Global {
		t.Error("Should find global product with global mode")
	}

	var productEN Product
	dbEN.First(&productEN, product.ID)
	if productEN.LanguageCode != l10n.Global {
		t.Error("Should find global product for en with mixed mode")
	}

	if !dbEN.Set("l10n:mode", "locale").First(&productEN, product.ID).RecordNotFound() {
		t.Error("Should find no record with locale mode")
	}

	if dbEN.Set("l10n:mode", "global").First(&productEN); productEN.LanguageCode != l10n.Global {
		t.Error("Should find global product with global mode")
	}
}

func TestDelete(t *testing.T) {
	product := Product{Code: "Delete", Name: "global"}
	dbGlobal.Create(&product)
	dbCN.Create(&product)

	if dbCN.Delete(&product).RowsAffected != 1 {
		t.Errorf("Should delete localized record")
	}

	if dbEN.Delete(&product).RowsAffected != 0 {
		t.Errorf("Should delete none record in unlocalized locale")
	}
}

func TestResetLanguageCodeWithGlobalDB(t *testing.T) {
	product := Product{Code: "Query", Name: "global"}
	product.LanguageCode = "test"
	dbGlobal.Save(&product)
	if product.LanguageCode != l10n.Global {
		t.Error("Should reset language code in global mode")
	}
}

func TestManyToManyRelations(t *testing.T) {
	product := Product{Code: "Delete", Name: "global", Tags: []Tag{{Name: "tag1"}, {Name: "tag2"}}}
	dbGlobal.Debug().Save(&product)
}
<commit_msg>Fix l10n tests<commit_after>package l10n_test

import (
	"testing"

	"github.com/jinzhu/gorm"
	_ "github.com/mattn/go-sqlite3"
	"github.com/qor/qor/l10n"

	_ "github.com/go-sql-driver/mysql"
)

func checkHasErr(t *testing.T, err error) {
	if err != nil {
		t.Error(err)
	}
}

func checkHasProductInLocale(db *gorm.DB, locale string, t *testing.T) {
	var count int
	if db.Set("l10n:locale", locale).Count(&count); count != 1 {
		t.Errorf("should has only one product for locale %v, but found %v", locale, count)
	}
}

func checkHasProductInAllLocales(db *gorm.DB, t *testing.T) {
	checkHasProductInLocale(db, l10n.Global, t)
	checkHasProductInLocale(db, "zh", t)
	checkHasProductInLocale(db, "en", t)
}

func TestCreateWithCreate(t *testing.T) {
	product := Product{Code: "CreateWithCreate"}
	checkHasErr(t, dbGlobal.Create(&product).Error)
	checkHasErr(t, dbCN.Create(&product).Error)
	checkHasErr(t, dbEN.Create(&product).Error)

	checkHasProductInAllLocales(dbGlobal.Model(&Product{}).Where("id = ? AND code = ?", product.ID, "CreateWithCreate"), t)
}

func TestCreateWithSave(t *testing.T) {
	product := Product{Code: "CreateWithSave"}
	checkHasErr(t, dbGlobal.Create(&product).Error)
	checkHasErr(t, dbCN.Create(&product).Error)
	checkHasErr(t, dbEN.Create(&product).Error)

	checkHasProductInAllLocales(dbGlobal.Model(&Product{}).Where("id = ? AND code = ?", product.ID, "CreateWithSave"), t)
}

func TestUpdate(t *testing.T) {
	product := Product{Code: "Update", Name: "global"}
	checkHasErr(t, dbGlobal.Create(&product).Error)
	sharedDB := dbGlobal.Model(&Product{}).Where("id = ? AND code = ?", product.ID, "Update")

	product.Name = "中文名"
	checkHasErr(t, dbCN.Create(&product).Error)
	checkHasProductInLocale(sharedDB.Where("name = ?", "中文名"), "zh", t)

	product.Name = "English Name"
	checkHasErr(t, dbEN.Create(&product).Error)
	checkHasProductInLocale(sharedDB.Where("name = ?", "English Name"), "en", t)

	product.Name = "新的中文名"
	product.Code = "NewCode // should be ignored when update"
	dbCN.Save(&product)
	checkHasProductInLocale(sharedDB.Where("name = ?", "新的中文名"), "zh", t)

	product.Name = "New English Name"
	product.Code = "NewCode // should be ignored when update"
	dbEN.Save(&product)
	checkHasProductInLocale(sharedDB.Where("name = ?", "New English Name"), "en", t)
}

func TestQuery(t *testing.T) {
	product := Product{Code: "Query", Name: "global"}
	dbGlobal.Create(&product)
	dbCN.Create(&product)

	var productCN Product
	dbCN.First(&productCN, product.ID)
	if productCN.LanguageCode != "zh" {
		t.Error("Should find localized zh product with mixed mode")
	}

	if dbCN.Set("l10n:mode", "locale").First(&productCN, product.ID).RecordNotFound() {
		t.Error("Should find localized zh product with locale mode")
	}

	if dbCN.Set("l10n:mode", "global").First(&productCN); productCN.LanguageCode != l10n.Global {
		t.Error("Should find global product with global mode")
	}

	var productEN Product
	dbEN.First(&productEN, product.ID)
	if productEN.LanguageCode != l10n.Global {
		t.Error("Should find global product for en with mixed mode")
	}

	if !dbEN.Set("l10n:mode", "locale").First(&productEN, product.ID).RecordNotFound() {
		t.Error("Should find no record with locale mode")
	}

	if dbEN.Set("l10n:mode", "global").First(&productEN); productEN.LanguageCode != l10n.Global {
		t.Error("Should find global product with global mode")
	}
}

func TestDelete(t *testing.T) {
	product := Product{Code: "Delete", Name: "global"}
	dbGlobal.Create(&product)
	dbCN.Create(&product)

	if dbCN.Delete(&product).RowsAffected != 1 {
		t.Errorf("Should delete localized record")
	}

	if dbEN.Delete(&product).RowsAffected != 0 {
		t.Errorf("Should delete none record in unlocalized locale")
	}
}

func TestResetLanguageCodeWithGlobalDB(t *testing.T) {
	product := Product{Code: "Query", Name: "global"}
	product.LanguageCode = "test"
	dbGlobal.Save(&product)
	if product.LanguageCode != l10n.Global {
		t.Error("Should reset language code in global mode")
	}
}

func TestManyToManyRelations(t *testing.T) {
	product := Product{Code: "Delete", Name: "global", Tags: []Tag{{Name: "tag1"}, {Name: "tag2"}}}
	dbGlobal.Debug().Save(&product)
}
<|endoftext|>


<commit_before><commit_msg>Re-add file-existence check<commit_after><|endoftext|>


<commit_before>package slack

import (
	"errors"
	"net/url"
	"strconv"
	"strings"
)

const (
	// Add here the defaults in the siten
	DEFAULT_FILES_USERID  = ""
	DEFAULT_FILES_TS_FROM = 0
	DEFAULT_FILES_TS_TO   = -1
	DEFAULT_FILES_TYPES   = "all"
	DEFAULT_FILES_COUNT   = 100
	DEFAULT_FILES_PAGE    = 1
)

// Comment contains all the information relative to a comment
type Comment struct {
	Id        string   `json:"id"`
	Timestamp JSONTime `json:"timestamp"`
	UserId    string   `json:"user"`
	Comment   string   `json:"comment"`
	Created   JSONTime `json:"created,omitempty"`
}

// File contains all the information for a file
type File struct {
	Id        string   `json:"id"`
	Created   JSONTime `json:"created"`
	Timestamp JSONTime `json:"timestamp"`

	Name       string `json:"name"`
	Title      string `json:"title"`
	Mimetype   string `json:"mimetype"`
	Filetype   string `json:"filetype"`
	PrettyType string `json:"pretty_type"`
	UserId     string `json:"user"`

	Mode         string `json:"mode"`
	Editable     bool   `json:"editable"`
	IsExternal   bool   `json:"is_external"`
	ExternalType string `json:"external_type"`

	Size int `json:"size"`

	URL                string `json:"url"`
	URLDownload        string `json:"url_download"`
	URLPrivate         string `json:"url_private"`
	URLPrivateDownload string `json:"url_private_download"`

	Thumb64     string `json:"thumb_64"`
	Thumb80     string `json:"thumb_80"`
	Thumb360    string `json:"thumb_360"`
	Thumb360Gif string `json:"thumb_360_gif"`
	Thumb360W   string `json:"thumb_360_w"`
	Thumb360H   string `json:"thumb_360_h"`

	Permalink        string `json:"permalink"`
	EditLink         string `json:"edit_link"`
	Preview          string `json:"preview"`
	PreviewHighlight string `json:"preview_highlight"`
	Lines            int    `json:"lines"`
	LinesMore        int    `json:"lines_more"`

	IsPublic        bool     `json:"is_public"`
	PublicURLShared bool     `json:"public_url_shared"`
	Channels        []string `json:"channels"`
	Groups          []string `json:"groups"`
	InitialComment  Comment  `json:"initial_comment"`
	NumStars        int      `json:"num_stars"`
	IsStarred       bool     `json:"is_starred"`
}

// FileUploadParameters contains all the parameters necessary (including the optional ones) for an UploadFile() request
type FileUploadParameters struct {
	File           string
	Content        string
	Filetype       string
	Filename       string
	Title          string
	InitialComment string
	Channels       []string
}

// GetFilesParameters contains all the parameters necessary (including the optional ones) for a GetFiles() request
type GetFilesParameters struct {
	UserId        string
	TimestampFrom JSONTime
	TimestampTo   JSONTime
	Types         string
	Count         int
	Page          int
}

type fileResponseFull struct {
	File     `json:"file"`
	Paging   `json:"paging"`
	Comments []Comment `json:"comments"`
	Files    []File    `json:"files"`

	SlackResponse
}

// NewGetFilesParameters provides an instance of GetFilesParameters with all the sane default values set
func NewGetFilesParameters() GetFilesParameters {
	return GetFilesParameters{
		UserId:        DEFAULT_FILES_USERID,
		TimestampFrom: DEFAULT_FILES_TS_FROM,
		TimestampTo:   DEFAULT_FILES_TS_TO,
		Types:         DEFAULT_FILES_TYPES,
		Count:         DEFAULT_FILES_COUNT,
		Page:          DEFAULT_FILES_PAGE,
	}
}

func fileRequest(path string, values url.Values, debug bool) (*fileResponseFull, error) {
	response := &fileResponseFull{}
	err := parseResponse(path, values, response, debug)
	if err != nil {
		return nil, err
	}
	if !response.Ok {
		return nil, errors.New(response.Error)
	}
	return response, nil
}

// GetFileInfo retrieves a file and related comments
func (api *Slack) GetFileInfo(fileId string, count, page int) (*File, []Comment, *Paging, error) {
	values := url.Values{
		"token": {api.config.token},
		"file":  {fileId},
		"count": {strconv.Itoa(count)},
		"page":  {strconv.Itoa(page)},
	}
	response, err := fileRequest("files.info", values, api.debug)
	if err != nil {
		return nil, nil, nil, err
	}
	return &response.File, response.Comments, &response.Paging, nil
}

// GetFiles retrieves all files according to the parameters given
func (api *Slack) GetFiles(params GetFilesParameters) ([]File, *Paging, error) {
	values := url.Values{
		"token": {api.config.token},
	}
	if params.UserId != DEFAULT_FILES_USERID {
		values.Add("user", params.UserId)
	}
	// XXX: this is broken. fix it with a proper unix timestamp
	if params.TimestampFrom != DEFAULT_FILES_TS_FROM {
		values.Add("ts_from", params.TimestampFrom.String())
	}
	if params.TimestampTo != DEFAULT_FILES_TS_TO {
		values.Add("ts_to", params.TimestampTo.String())
	}
	if params.Types != DEFAULT_FILES_TYPES {
		values.Add("types", params.Types)
	}
	if params.Count != DEFAULT_FILES_COUNT {
		values.Add("count", strconv.Itoa(params.Count))
	}
	if params.Page != DEFAULT_FILES_PAGE {
		values.Add("page", strconv.Itoa(params.Page))
	}
	response, err := fileRequest("files.list", values, api.debug)
	if err != nil {
		return nil, nil, err
	}
	return response.Files, &response.Paging, nil
}

// UploadFile uploads a file
func (api *Slack) UploadFile(params FileUploadParameters) (file *File, err error) {
	// Test if user token is valid. This helps because client.Do doesn't like this for some reason. XXX: More
	// investigation needed, but for now this will do.
	_, err = api.AuthTest()
	if err != nil {
		return nil, err
	}
	response := &fileResponseFull{}
	values := url.Values{
		"token": {api.config.token},
	}
	if params.Filetype != "" {
		values.Add("filetype", params.Filetype)
	}
	if params.Filename != "" {
		values.Add("filename", params.Filename)
	}
	if params.Title != "" {
		values.Add("title", params.Title)
	}
	if params.InitialComment != "" {
		values.Add("initial_comment", params.InitialComment)
	}
	if len(params.Channels) != 0 {
		values.Add("channels", strings.Join(params.Channels, ","))
	}
	if params.Content != "" {
		values.Add("content", params.Content)
		err = parseResponse("files.upload", values, response, api.debug)
	} else if params.File != "" {
		err = parseResponseMultipart("files.upload", params.File, values, response, api.debug)
	}
	if err != nil {
		return nil, err
	}
	if !response.Ok {
		return nil, errors.New(response.Error)
	}
	return &response.File, nil
}

// DeleteFile deletes a file
func (api *Slack) DeleteFile(fileId string) error {
	values := url.Values{
		"token": {api.config.token},
		"file":  {fileId},
	}
	_, err := fileRequest("files.delete", values, api.debug)
	if err != nil {
		return err
	}
	return nil

}
<commit_msg>Change type of Thumb360W and Thumb360H to int<commit_after>package slack

import (
	"errors"
	"net/url"
	"strconv"
	"strings"
)

const (
	// Add here the defaults in the siten
	DEFAULT_FILES_USERID  = ""
	DEFAULT_FILES_TS_FROM = 0
	DEFAULT_FILES_TS_TO   = -1
	DEFAULT_FILES_TYPES   = "all"
	DEFAULT_FILES_COUNT   = 100
	DEFAULT_FILES_PAGE    = 1
)

// Comment contains all the information relative to a comment
type Comment struct {
	Id        string   `json:"id"`
	Timestamp JSONTime `json:"timestamp"`
	UserId    string   `json:"user"`
	Comment   string   `json:"comment"`
	Created   JSONTime `json:"created,omitempty"`
}

// File contains all the information for a file
type File struct {
	Id        string   `json:"id"`
	Created   JSONTime `json:"created"`
	Timestamp JSONTime `json:"timestamp"`

	Name       string `json:"name"`
	Title      string `json:"title"`
	Mimetype   string `json:"mimetype"`
	Filetype   string `json:"filetype"`
	PrettyType string `json:"pretty_type"`
	UserId     string `json:"user"`

	Mode         string `json:"mode"`
	Editable     bool   `json:"editable"`
	IsExternal   bool   `json:"is_external"`
	ExternalType string `json:"external_type"`

	Size int `json:"size"`

	URL                string `json:"url"`
	URLDownload        string `json:"url_download"`
	URLPrivate         string `json:"url_private"`
	URLPrivateDownload string `json:"url_private_download"`

	Thumb64     string `json:"thumb_64"`
	Thumb80     string `json:"thumb_80"`
	Thumb360    string `json:"thumb_360"`
	Thumb360Gif string `json:"thumb_360_gif"`
	Thumb360W   int    `json:"thumb_360_w"`
	Thumb360H   int    `json:"thumb_360_h"`

	Permalink        string `json:"permalink"`
	EditLink         string `json:"edit_link"`
	Preview          string `json:"preview"`
	PreviewHighlight string `json:"preview_highlight"`
	Lines            int    `json:"lines"`
	LinesMore        int    `json:"lines_more"`

	IsPublic        bool     `json:"is_public"`
	PublicURLShared bool     `json:"public_url_shared"`
	Channels        []string `json:"channels"`
	Groups          []string `json:"groups"`
	InitialComment  Comment  `json:"initial_comment"`
	NumStars        int      `json:"num_stars"`
	IsStarred       bool     `json:"is_starred"`
}

// FileUploadParameters contains all the parameters necessary (including the optional ones) for an UploadFile() request
type FileUploadParameters struct {
	File           string
	Content        string
	Filetype       string
	Filename       string
	Title          string
	InitialComment string
	Channels       []string
}

// GetFilesParameters contains all the parameters necessary (including the optional ones) for a GetFiles() request
type GetFilesParameters struct {
	UserId        string
	TimestampFrom JSONTime
	TimestampTo   JSONTime
	Types         string
	Count         int
	Page          int
}

type fileResponseFull struct {
	File     `json:"file"`
	Paging   `json:"paging"`
	Comments []Comment `json:"comments"`
	Files    []File    `json:"files"`

	SlackResponse
}

// NewGetFilesParameters provides an instance of GetFilesParameters with all the sane default values set
func NewGetFilesParameters() GetFilesParameters {
	return GetFilesParameters{
		UserId:        DEFAULT_FILES_USERID,
		TimestampFrom: DEFAULT_FILES_TS_FROM,
		TimestampTo:   DEFAULT_FILES_TS_TO,
		Types:         DEFAULT_FILES_TYPES,
		Count:         DEFAULT_FILES_COUNT,
		Page:          DEFAULT_FILES_PAGE,
	}
}

func fileRequest(path string, values url.Values, debug bool) (*fileResponseFull, error) {
	response := &fileResponseFull{}
	err := parseResponse(path, values, response, debug)
	if err != nil {
		return nil, err
	}
	if !response.Ok {
		return nil, errors.New(response.Error)
	}
	return response, nil
}

// GetFileInfo retrieves a file and related comments
func (api *Slack) GetFileInfo(fileId string, count, page int) (*File, []Comment, *Paging, error) {
	values := url.Values{
		"token": {api.config.token},
		"file":  {fileId},
		"count": {strconv.Itoa(count)},
		"page":  {strconv.Itoa(page)},
	}
	response, err := fileRequest("files.info", values, api.debug)
	if err != nil {
		return nil, nil, nil, err
	}
	return &response.File, response.Comments, &response.Paging, nil
}

// GetFiles retrieves all files according to the parameters given
func (api *Slack) GetFiles(params GetFilesParameters) ([]File, *Paging, error) {
	values := url.Values{
		"token": {api.config.token},
	}
	if params.UserId != DEFAULT_FILES_USERID {
		values.Add("user", params.UserId)
	}
	// XXX: this is broken. fix it with a proper unix timestamp
	if params.TimestampFrom != DEFAULT_FILES_TS_FROM {
		values.Add("ts_from", params.TimestampFrom.String())
	}
	if params.TimestampTo != DEFAULT_FILES_TS_TO {
		values.Add("ts_to", params.TimestampTo.String())
	}
	if params.Types != DEFAULT_FILES_TYPES {
		values.Add("types", params.Types)
	}
	if params.Count != DEFAULT_FILES_COUNT {
		values.Add("count", strconv.Itoa(params.Count))
	}
	if params.Page != DEFAULT_FILES_PAGE {
		values.Add("page", strconv.Itoa(params.Page))
	}
	response, err := fileRequest("files.list", values, api.debug)
	if err != nil {
		return nil, nil, err
	}
	return response.Files, &response.Paging, nil
}

// UploadFile uploads a file
func (api *Slack) UploadFile(params FileUploadParameters) (file *File, err error) {
	// Test if user token is valid. This helps because client.Do doesn't like this for some reason. XXX: More
	// investigation needed, but for now this will do.
	_, err = api.AuthTest()
	if err != nil {
		return nil, err
	}
	response := &fileResponseFull{}
	values := url.Values{
		"token": {api.config.token},
	}
	if params.Filetype != "" {
		values.Add("filetype", params.Filetype)
	}
	if params.Filename != "" {
		values.Add("filename", params.Filename)
	}
	if params.Title != "" {
		values.Add("title", params.Title)
	}
	if params.InitialComment != "" {
		values.Add("initial_comment", params.InitialComment)
	}
	if len(params.Channels) != 0 {
		values.Add("channels", strings.Join(params.Channels, ","))
	}
	if params.Content != "" {
		values.Add("content", params.Content)
		err = parseResponse("files.upload", values, response, api.debug)
	} else if params.File != "" {
		err = parseResponseMultipart("files.upload", params.File, values, response, api.debug)
	}
	if err != nil {
		return nil, err
	}
	if !response.Ok {
		return nil, errors.New(response.Error)
	}
	return &response.File, nil
}

// DeleteFile deletes a file
func (api *Slack) DeleteFile(fileId string) error {
	values := url.Values{
		"token": {api.config.token},
		"file":  {fileId},
	}
	_, err := fileRequest("files.delete", values, api.debug)
	if err != nil {
		return err
	}
	return nil

}
<|endoftext|>


<commit_before>// Copyright 2019, 2020 Tamás Gulácsi
//
//
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.

package grpcer

import (
	"bytes"
	"fmt"
	//json "encoding/json"
	"errors"
	"io"
	"io/ioutil"
	"os"
	"reflect"
	"strings"

	json "github.com/json-iterator/go"
)

var errNewField = errors.New("new field")

func mergeStreams(w io.Writer, first interface{}, recv interface {
	Recv() (interface{}, error)
},
	Log func(...interface{}) error,
) error {
	if Log == nil {
		Log = func(...interface{}) error { return nil }
	}

	slice, notSlice := sliceFields(first)
	if len(slice) == 0 {
		var err error
		part := first
		enc := json.NewEncoder(w)
		for {
			if err := enc.Encode(part); err != nil {
				Log("encode", part, "error", err)
				return fmt.Errorf("encode part: %w", err)
			}

			part, err = recv.Recv()
			if err != nil {
				if err != io.EOF {
					Log("msg", "recv", "error", err)
				}
				break
			}
		}
		Log("slice", len(slice))
		return nil
	}

	names := make(map[string]bool, len(slice)+len(notSlice))

	buf := bufPool.Get().(*bytes.Buffer)
	defer func() {
		buf.Reset()
		bufPool.Put(buf)
	}()
	jenc := json.NewEncoder(buf)

	//Log("slices", slice)
	w.Write([]byte("{"))
	for _, f := range notSlice {
		buf.Reset()
		jenc.Encode(f.JSONName)
		w.Write(bytes.TrimSpace(buf.Bytes()))

		w.Write([]byte{':'})
		buf.Reset()
		jenc.Encode(f.Value)
		w.Write(bytes.TrimSpace(buf.Bytes()))
		w.Write([]byte{','})

		names[f.Name] = false
	}
	buf.Reset()
	jenc.Encode(slice[0].JSONName)
	w.Write(bytes.TrimSpace(buf.Bytes()))
	w.Write([]byte(":"))

	buf.Reset()
	jenc.Encode(slice[0].Value)
	w.Write(bytes.TrimSuffix(bytes.TrimSpace(buf.Bytes()), []byte{']'}))

	names[slice[0].Name] = true

	files := make(map[string]*os.File, len(slice)-1)
	for _, f := range slice[1:] {
		fh, err := ioutil.TempFile("", "merge-"+f.Name+"-")
		if err != nil {
			Log("tempFile", f.Name, "error", err)
			return fmt.Errorf("%s: %w", f.Name, err)
		}
		os.Remove(fh.Name())
		Log("fn", fh.Name())
		defer fh.Close()
		files[f.Name] = fh
		buf.Reset()
		jenc.Encode(f.JSONName)
		fh.Write(bytes.TrimSpace(buf.Bytes()))
		io.WriteString(fh, ":[")

		buf.Reset()
		jenc.Encode(f.Value)
		fh.Write(trimSqBrs(buf.Bytes()))

		names[f.Name] = true
	}

	var part interface{}
	var err error
	for {
		part, err = recv.Recv()
		if err != nil {
			if err != io.EOF {
				Log("msg", "recv", "error", err)
			}
			break
		}
		buf.Reset()
		jenc.Encode(part)
		Log("part", limitWidth(buf.Bytes(), 256))

		S, nS := sliceFields(part)
		for _, f := range S {
			if isSlice, ok := names[f.Name]; !(ok && isSlice) {
				err = fmt.Errorf("%s: %w", f.Name, errNewField)
				break
			}
		}
		for _, f := range nS {
			if isSlice, ok := names[f.Name]; !(ok && !isSlice) {
				err = fmt.Errorf("%s: %w", f.Name, errNewField)
				break
			}
		}
		if len(S) == 0 {
			break
		}
		if err != nil {
			Log("error", err)
			//TODO(tgulacsi): close the merge and send as is
			return err
		}

		if S[0].Name == slice[0].Name {
			w.Write([]byte{','})
			buf.Reset()
			jenc.Encode(S[0].Value)
			w.Write(trimSqBrs(buf.Bytes()))
			S = S[1:]
		}
		for _, f := range S {
			fh := files[f.Name]
			if _, err := fh.Write([]byte{','}); err != nil {
				Log("write", fh.Name(), "error", err)
			}
			buf.Reset()
			jenc.Encode(f.Value)
			fh.Write(trimSqBrs(buf.Bytes()))
		}
	}
	w.Write([]byte("]"))

	for _, fh := range files {
		if _, err := fh.Seek(0, 0); err != nil {
			Log("Seek", fh.Name(), "error", err)
			continue
		}
		w.Write([]byte{','})
		io.Copy(w, fh)
		w.Write([]byte{']'})
	}
	w.Write([]byte{'}', '\n'})
	return nil
}

type field struct {
	Name     string
	JSONName string
	Value    interface{}
}

func sliceFields(part interface{}) (slice, notSlice []field) {
	rv := reflect.ValueOf(part)
	t := rv.Type()
	if t.Kind() == reflect.Ptr {
		rv = rv.Elem()
		t = rv.Type()
	}
	n := t.NumField()
	for i := 0; i < n; i++ {
		f := rv.Field(i)
		tf := t.Field(i)
		fld := field{Name: tf.Name, Value: f.Interface()}
		fld.JSONName = tf.Tag.Get("json")
		if i := strings.IndexByte(fld.JSONName, ','); i >= 0 {
			fld.JSONName = fld.JSONName[:i]
		}
		if fld.JSONName == "" {
			fld.JSONName = fld.Name
		}

		if f.Type().Kind() != reflect.Slice {
			notSlice = append(notSlice, fld)
			continue
		}
		if f.IsNil() {
			continue
		}
		slice = append(slice, fld)
	}
	return slice, notSlice
}
func trimSqBrs(b []byte) []byte {
	b = bytes.TrimSpace(b)
	if len(b) == 0 {
		return b
	}
	if b[0] == '[' {
		b = b[1:]
	}
	if len(b) == 0 {
		return b
	}
	if b[len(b)-1] == ']' {
		b = b[:len(b)-1]
	}
	return b
}
<commit_msg>Lax merging rules - allow unseen slices<commit_after>// Copyright 2019, 2020 Tamás Gulácsi
//
//
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.

package grpcer

import (
	"bytes"
	"fmt"
	//json "encoding/json"
	"errors"
	"io"
	"io/ioutil"
	"os"
	"reflect"
	"strings"

	json "github.com/json-iterator/go"
)

var (
	errNewField = errors.New("new field")
	errWrongType = errors.New("wrong type")
)

func mergeStreams(w io.Writer, first interface{}, recv interface{ Recv() (interface{}, error) }, Log func(...interface{}) error) error {
	if Log == nil {
		Log = func(...interface{}) error { return nil }
	}

	slice, notSlice := sliceFields(first)
	if len(slice) == 0 {
		var err error
		part := first
		enc := json.NewEncoder(w)
		for {
			if err := enc.Encode(part); err != nil {
				Log("encode", part, "error", err)
				return fmt.Errorf("encode part: %w", err)
			}

			part, err = recv.Recv()
			if err != nil {
				if err != io.EOF {
					Log("msg", "recv", "error", err)
				}
				break
			}
		}
		Log("slice", len(slice))
		return nil
	}

	names := make(map[string]bool, len(slice)+len(notSlice))

	buf := bufPool.Get().(*bytes.Buffer)
	defer func() {
		buf.Reset()
		bufPool.Put(buf)
	}()
	jenc := json.NewEncoder(buf)

	//Log("slices", slice)
	w.Write([]byte("{"))
	for _, f := range notSlice {
		buf.Reset()
		jenc.Encode(f.JSONName)
		w.Write(bytes.TrimSpace(buf.Bytes()))

		w.Write([]byte{':'})
		buf.Reset()
		jenc.Encode(f.Value)
		w.Write(bytes.TrimSpace(buf.Bytes()))
		w.Write([]byte{','})

		names[f.Name] = false
	}
	buf.Reset()
	jenc.Encode(slice[0].JSONName)
	w.Write(bytes.TrimSpace(buf.Bytes()))
	w.Write([]byte(":"))

	buf.Reset()
	jenc.Encode(slice[0].Value)
	w.Write(bytes.TrimSuffix(bytes.TrimSpace(buf.Bytes()), []byte{']'}))

	names[slice[0].Name] = true
	files := make(map[string]*os.File, len(slice)-1)
	openFile := func(f field) error {
		fh, err := ioutil.TempFile("", "merge-"+f.Name+"-")
		if err != nil {
			Log("tempFile", f.Name, "error", err)
			return fmt.Errorf("%s: %w", f.Name, err)
		}
		os.Remove(fh.Name())
		Log("fn", fh.Name())
		files[f.Name] = fh
		buf.Reset()
		jenc.Encode(f.JSONName)
		fh.Write(bytes.TrimSpace(buf.Bytes()))
		io.WriteString(fh, ":[")

		buf.Reset()
		jenc.Encode(f.Value)
		fh.Write(trimSqBrs(buf.Bytes()))

		names[f.Name] = true
		return nil
	}
	defer func()  {
		for nm, fh := range files {
			fh.Close()
			delete(files, nm)
		}
	}()

	for _, f := range slice[1:] {
		if err := openFile(f); err != nil {
			return err
		}
	}

	var part interface{}
	var err error
	for {
		part, err = recv.Recv()
		if err != nil {
			if err != io.EOF {
				Log("msg", "recv", "error", err)
			}
			break
		}
		buf.Reset()
		jenc.Encode(part)
		Log("part", limitWidth(buf.Bytes(), 256))

		S, nS := sliceFields(part)
		for _, f := range S {
			if isSlice, ok := names[f.Name]; !ok {
				if err = openFile(f); err != nil {
					break
				}
				//err = fmt.Errorf("%s: %w", f.Name, errNewField)
				//break
			} else if !isSlice {
				err = fmt.Errorf("%s not slice: %w", f.Name, errWrongType)
				break
			}
		}
		for _, f := range nS {
			if isSlice, ok := names[f.Name]; !ok {
				err = fmt.Errorf("%s: %w", f.Name, errNewField)
				break
			} else if isSlice {
				err = fmt.Errorf("%s slice: %w", f.Name, errWrongType)
				break
			}
		}
		if len(S) == 0 {
			break
		}
		if err != nil {
			Log("error", err)
			//TODO(tgulacsi): close the merge and send as is
			return err
		}

		if S[0].Name == slice[0].Name {
			w.Write([]byte{','})
			buf.Reset()
			jenc.Encode(S[0].Value)
			w.Write(trimSqBrs(buf.Bytes()))
			S = S[1:]
		}
		for _, f := range S {
			fh := files[f.Name]
			if _, err := fh.Write([]byte{','}); err != nil {
				if Log != nil {
					Log("write", fh.Name(), "error", err)
				}
			}
			buf.Reset()
			jenc.Encode(f.Value)
			fh.Write(trimSqBrs(buf.Bytes()))
		}
	}
	w.Write([]byte("]"))

	for nm, fh := range files {
		if _, err := fh.Seek(0, 0); err != nil {
			Log("Seek", fh.Name(), "error", err)
			continue
		}
		w.Write([]byte{','})
		io.Copy(w, fh)
		w.Write([]byte{']'})
		fh.Close()
		delete(files, nm)
	}
	w.Write([]byte{'}', '\n'})
	return nil
}

type field struct {
	Name     string
	JSONName string
	Value    interface{}
}

func sliceFields(part interface{}) (slice, notSlice []field) {
	rv := reflect.ValueOf(part)
	t := rv.Type()
	if t.Kind() == reflect.Ptr {
		rv = rv.Elem()
		t = rv.Type()
	}
	n := t.NumField()
	for i := 0; i < n; i++ {
		f := rv.Field(i)
		tf := t.Field(i)
		fld := field{Name: tf.Name, Value: f.Interface()}
		fld.JSONName = tf.Tag.Get("json")
		if i := strings.IndexByte(fld.JSONName, ','); i >= 0 {
			fld.JSONName = fld.JSONName[:i]
		}
		if fld.JSONName == "" {
			fld.JSONName = fld.Name
		}

		if f.Type().Kind() != reflect.Slice {
			notSlice = append(notSlice, fld)
			continue
		}
		if f.IsNil() {
			continue
		}
		slice = append(slice, fld)
	}
	return slice, notSlice
}
func trimSqBrs(b []byte) []byte {
	b = bytes.TrimSpace(b)
	if len(b) == 0 {
		return b
	}
	if b[0] == '[' {
		b = b[1:]
	}
	if len(b) == 0 {
		return b
	}
	if b[len(b)-1] == ']' {
		b = b[:len(b)-1]
	}
	return b
}
<|endoftext|>


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

// Based on src/pkg/reflect/deepequal.go from official
// golang's stdlib.

package mergo

import (
	"fmt"
	"reflect"
	"unsafe"
)

func hasExportedField(dst reflect.Value) bool {
	for i, n := 0, dst.NumField(); i < n; i++ {
		field := dst.Type().Field(i)
		if isExportedComponent(&field) {
			return true
		}
	}

	return false
}

func isExportedComponent(field *reflect.StructField) bool {
	pkgPath := field.PkgPath
	if len(pkgPath) > 0 {
		return false
	}

	c := field.Name[0]
	if 'a' <= c && c <= 'z' || c == '_' {
		return false
	}

	return true
}

// Config allows to customize Mergo's behaviour.
type Config struct {
	Overwrite                    bool
	AppendSlice                  bool
	TypeCheck                    bool
	overwriteWithEmptyValue      bool
	overwriteSliceWithEmptyValue bool
	deepMergeSlice               bool
	Transformers                 Transformers
}

// Transformers allow to merge specific types differently than in the default behavior.
// In other words, now you can customize how some types are merged.
type Transformers interface {
	Transformer(reflect.Type) func(dst, src reflect.Value) error
}

// Traverses recursively both values, assigning src's fields values to dst.
// The map argument tracks comparisons that have already been seen, which allows
// short circuiting on recursive types.
func deepMerge(dstIn, src reflect.Value, visited map[uintptr]*visit, depth int, config *Config) (dst reflect.Value, err error) {
	dst = dstIn
	overwrite := config.Overwrite
	typeCheck := config.TypeCheck
	overwriteWithEmptySrc := config.overwriteWithEmptyValue
	overwriteSliceWithEmptySrc := config.overwriteSliceWithEmptyValue

	if !src.IsValid() {
		return
	}

	if dst.CanAddr() {
		addr := dst.UnsafeAddr()
		h := 17 * addr
		typ := dst.Type()
		seen := visited[h]

		for p := seen; p != nil; p = p.next {
			if p.ptr == addr && p.typ == typ {
				return
			}
		}

		// Remember, remember...
		visited[h] = &visit{addr, typ, seen}
	}

	if config.Transformers != nil && !isEmptyValue(dst) {
		if fn := config.Transformers.Transformer(dst.Type()); fn != nil {
			err = fn(dst, src)
			return
		}
	}

	if dst.IsValid() && src.IsValid() && src.Type() != dst.Type() {
		err = fmt.Errorf("cannot append two different types (%s, %s)", src.Kind(), dst.Kind())
		return
	}

	switch dst.Kind() {
	case reflect.Struct:
		if hasExportedField(dst) {
			dstCp := reflect.New(dst.Type()).Elem()

			for i, n := 0, dst.NumField(); i < n; i++ {
				dstField := dst.Field(i)
				structField := dst.Type().Field(i)
				// copy un-exported struct fields
				if !isExportedComponent(&structField) {
					rf := dstCp.Field(i)
					rf = reflect.NewAt(rf.Type(), unsafe.Pointer(rf.UnsafeAddr())).Elem() //nolint:gosec

					dstRF := dst.Field(i)
					if !dstRF.CanAddr() {
						continue
					}

					dstRF = reflect.NewAt(dstRF.Type(), unsafe.Pointer(dstRF.UnsafeAddr())).Elem() //nolint:gosec
					rf.Set(dstRF)

					continue
				}

				dstField, err = deepMerge(dstField, src.Field(i), visited, depth+1, config)
				if err != nil {
					return
				}

				dstCp.Field(i).Set(dstField)
			}

			if dst.CanSet() {
				dst.Set(dstCp)
			} else {
				dst = dstCp
			}

			return
		}

		if (isReflectNil(dst) || overwrite) && (!isEmptyValue(src) || overwriteWithEmptySrc) {
			dst = src
		}
	case reflect.Map:
		if dst.IsNil() && !src.IsNil() {
			if dst.CanSet() {
				dst.Set(reflect.MakeMap(dst.Type()))
			} else {
				dst = src
				return
			}
		}

		for _, key := range src.MapKeys() {
			srcElement := src.MapIndex(key)
			if !srcElement.IsValid() {
				continue
			}

			dstElement := dst.MapIndex(key)

			if dst.MapIndex(key).IsValid() {
				k := dstElement.Interface()
				dstElement = reflect.ValueOf(k)
			}

			if isReflectNil(srcElement) {
				if overwrite || isReflectNil(dstElement) {
					dst.SetMapIndex(key, srcElement)
				}

				continue
			}

			if !srcElement.CanInterface() {
				continue
			}

			if srcElement.CanInterface() {
				srcElement = reflect.ValueOf(srcElement.Interface())

				if dstElement.IsValid() {
					dstElement = reflect.ValueOf(dstElement.Interface())
				}
			}

			dstElement, err = deepMerge(dstElement, srcElement, visited, depth+1, config)
			if err != nil {
				return
			}

			dst.SetMapIndex(key, dstElement)
		}
	case reflect.Slice:
		newSlice := dst

		if (!isEmptyValue(src) || overwriteWithEmptySrc || overwriteSliceWithEmptySrc) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice {
			if typeCheck && src.Type() != dst.Type() {
				err = fmt.Errorf("cannot override two slices with different type (%s, %s)", src.Type(), dst.Type())
				return
			}

			newSlice = src
		} else if config.AppendSlice {
			if typeCheck && src.Type() != dst.Type() {
				err = fmt.Errorf("cannot append two slice with different type (%s, %s)", src.Type(), dst.Type())
				return
			}
			newSlice = reflect.AppendSlice(dst, src)
		} else if config.deepMergeSlice {
			if typeCheck && src.Type() != dst.Type() {
				err = fmt.Errorf("cannot deep merge two slice with different type (%s, %s)", src.Type(), dst.Type())
				return
			}

			if src.Len() > dst.Len() {
				newSlice = reflect.MakeSlice(dst.Type(), src.Len(), src.Len())

				for i := 0; i < dst.Len(); i++ {
					newSlice.Index(i).Set(dst.Index(i))
				}
			}

			for i := 0; i < src.Len(); i++ {
				srcElem := src.Index(i)
				dstElem := newSlice.Index(i)

				if srcElem.CanInterface() {
					srcElem = reflect.ValueOf(srcElem.Interface())
				}

				if dstElem.CanInterface() {
					dstElem = reflect.ValueOf(dstElem.Interface())
				}

				if newSlice.Index(i).IsZero() {
					newSlice.Index(i).Set(srcElem)
				} else {
					if dstElem, err = deepMerge(dstElem, srcElem, visited, depth+1, config); err != nil {
						return
					} else {
						newSlice.Index(i).Set(dstElem)
					}
				}
			}
		}

		if dst.CanSet() {
			dst.Set(newSlice)
		} else {
			dst = newSlice
		}
	case reflect.Ptr, reflect.Interface:
		if isReflectNil(src) {
			break
		}

		if dst.Kind() != reflect.Ptr && src.Type().AssignableTo(dst.Type()) {
			if dst.IsNil() || overwrite {
				if overwrite || isEmptyValue(dst) {
					if dst.CanSet() {
						dst.Set(src)
					} else {
						dst = src
					}
				}
			}

			break
		}

		if src.Kind() != reflect.Interface {
			if dst.IsNil() || (src.Kind() != reflect.Ptr && overwrite) {
				if dst.CanSet() && (overwrite || isEmptyValue(dst)) {
					dst.Set(src)
				}
			} else if src.Kind() == reflect.Ptr {
				if dst, err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil {
					return
				}
				dst = dst.Addr()
			} else if dst.Elem().Type() == src.Type() {
				if dst, err = deepMerge(dst.Elem(), src, visited, depth+1, config); err != nil {
					return
				}
			} else {
				err = ErrDifferentArgumentsTypes
				return
			}

			break
		}

		if dst.IsNil() || overwrite {
			if (overwrite || isEmptyValue(dst)) && (overwriteWithEmptySrc || !isEmptyValue(src)) {
				if dst.CanSet() {
					dst.Set(src)
				} else {
					dst = src
				}
			}
		} else if _, err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil {
			return
		}
	default:
		mustSet := (!isEmptyValue(src) || overwriteWithEmptySrc) && (overwrite || isEmptyValue(dst))
		if mustSet {
			if dst.CanSet() {
				dst.Set(src)
			} else {
				dst = src
			}
		}
	}

	return dst, nil
}

// Merge will fill any empty for value type attributes on the dst struct using corresponding
// src attributes if they themselves are not empty. dst and src must be valid same-type structs
// and dst must be a pointer to struct.
// It won't merge unexported (private) fields and will do recursively any exported field.
func Merge(dst, src interface{}, opts ...func(*Config)) error {
	return merge(dst, src, opts...)
}

// MergeWithOverwrite will do the same as Merge except that non-empty dst attributes will be overridden by
// non-empty src attribute values.
// Deprecated: use Merge(…) with WithOverride
func MergeWithOverwrite(dst, src interface{}, opts ...func(*Config)) error {
	return merge(dst, src, append(opts, WithOverride)...)
}

// WithTransformers adds transformers to merge, allowing to customize the merging of some types.
func WithTransformers(transformers Transformers) func(*Config) {
	return func(config *Config) {
		config.Transformers = transformers
	}
}

// WithOverride will make merge override non-empty dst attributes with non-empty src attributes values.
func WithOverride(config *Config) {
	config.Overwrite = true
}

// WithOverwriteWithEmptyValue will make merge override non empty dst attributes with empty src attributes values.
func WithOverwriteWithEmptyValue(config *Config) {
	config.Overwrite = true
	config.overwriteWithEmptyValue = true
}

// WithOverrideEmptySlice will make merge override empty dst slice with empty src slice.
func WithOverrideEmptySlice(config *Config) {
	config.overwriteSliceWithEmptyValue = true
}

// WithAppendSlice will make merge append slices instead of overwriting it.
func WithAppendSlice(config *Config) {
	config.AppendSlice = true
}

// WithTypeCheck will make merge check types while overwriting it (must be used with WithOverride).
func WithTypeCheck(config *Config) {
	config.TypeCheck = true
}

// WithDeepMergeSlice will make merge deep merge slice elements pairwise (resizing dst slice if needed)
func WithDeepMergeSlice(config *Config) {
	config.deepMergeSlice = true
}

func merge(dst, src interface{}, opts ...func(*Config)) error {
	if dst != nil && reflect.ValueOf(dst).Kind() != reflect.Ptr {
		return ErrNonPointerArgument
	}
	var (
		vDst, vSrc reflect.Value
		err        error
	)

	config := &Config{}

	for _, opt := range opts {
		opt(config)
	}

	if vDst, vSrc, err = resolveValues(dst, src); err != nil {
		return err
	}
	if !vDst.CanSet() {
		return fmt.Errorf("cannot set dst, needs reference")
	}
	if vDst.Type() != vSrc.Type() {
		return ErrDifferentArgumentsTypes
	}
	_, err = deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, config)
	return err
}

// IsReflectNil is the reflect value provided nil
func isReflectNil(v reflect.Value) bool {
	k := v.Kind()
	switch k {
	case reflect.Interface, reflect.Slice, reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr:
		// Both interface and slice are nil if first word is 0.
		// Both are always bigger than a word; assume flagIndir.
		return v.IsNil()
	default:
		return false
	}
}
<commit_msg>fix style issue raised by hound<commit_after>// Copyright 2013 Dario Castañé. All rights reserved.
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Based on src/pkg/reflect/deepequal.go from official
// golang's stdlib.

package mergo

import (
	"fmt"
	"reflect"
	"unsafe"
)

func hasExportedField(dst reflect.Value) bool {
	for i, n := 0, dst.NumField(); i < n; i++ {
		field := dst.Type().Field(i)
		if isExportedComponent(&field) {
			return true
		}
	}

	return false
}

func isExportedComponent(field *reflect.StructField) bool {
	pkgPath := field.PkgPath
	if len(pkgPath) > 0 {
		return false
	}

	c := field.Name[0]
	if 'a' <= c && c <= 'z' || c == '_' {
		return false
	}

	return true
}

// Config allows to customize Mergo's behaviour.
type Config struct {
	Overwrite                    bool
	AppendSlice                  bool
	TypeCheck                    bool
	overwriteWithEmptyValue      bool
	overwriteSliceWithEmptyValue bool
	deepMergeSlice               bool
	Transformers                 Transformers
}

// Transformers allow to merge specific types differently than in the default behavior.
// In other words, now you can customize how some types are merged.
type Transformers interface {
	Transformer(reflect.Type) func(dst, src reflect.Value) error
}

// Traverses recursively both values, assigning src's fields values to dst.
// The map argument tracks comparisons that have already been seen, which allows
// short circuiting on recursive types.
func deepMerge(dstIn, src reflect.Value, visited map[uintptr]*visit, depth int, config *Config) (dst reflect.Value, err error) {
	dst = dstIn
	overwrite := config.Overwrite
	typeCheck := config.TypeCheck
	overwriteWithEmptySrc := config.overwriteWithEmptyValue
	overwriteSliceWithEmptySrc := config.overwriteSliceWithEmptyValue

	if !src.IsValid() {
		return
	}

	if dst.CanAddr() {
		addr := dst.UnsafeAddr()
		h := 17 * addr
		typ := dst.Type()
		seen := visited[h]

		for p := seen; p != nil; p = p.next {
			if p.ptr == addr && p.typ == typ {
				return
			}
		}

		// Remember, remember...
		visited[h] = &visit{addr, typ, seen}
	}

	if config.Transformers != nil && !isEmptyValue(dst) {
		if fn := config.Transformers.Transformer(dst.Type()); fn != nil {
			err = fn(dst, src)
			return
		}
	}

	if dst.IsValid() && src.IsValid() && src.Type() != dst.Type() {
		err = fmt.Errorf("cannot append two different types (%s, %s)", src.Kind(), dst.Kind())
		return
	}

	switch dst.Kind() {
	case reflect.Struct:
		if hasExportedField(dst) {
			dstCp := reflect.New(dst.Type()).Elem()

			for i, n := 0, dst.NumField(); i < n; i++ {
				dstField := dst.Field(i)
				structField := dst.Type().Field(i)
				// copy un-exported struct fields
				if !isExportedComponent(&structField) {
					rf := dstCp.Field(i)
					rf = reflect.NewAt(rf.Type(), unsafe.Pointer(rf.UnsafeAddr())).Elem() //nolint:gosec

					dstRF := dst.Field(i)
					if !dstRF.CanAddr() {
						continue
					}

					dstRF = reflect.NewAt(dstRF.Type(), unsafe.Pointer(dstRF.UnsafeAddr())).Elem() //nolint:gosec
					rf.Set(dstRF)

					continue
				}

				dstField, err = deepMerge(dstField, src.Field(i), visited, depth+1, config)
				if err != nil {
					return
				}

				dstCp.Field(i).Set(dstField)
			}

			if dst.CanSet() {
				dst.Set(dstCp)
			} else {
				dst = dstCp
			}

			return
		}

		if (isReflectNil(dst) || overwrite) && (!isEmptyValue(src) || overwriteWithEmptySrc) {
			dst = src
		}
	case reflect.Map:
		if dst.IsNil() && !src.IsNil() {
			if dst.CanSet() {
				dst.Set(reflect.MakeMap(dst.Type()))
			} else {
				dst = src
				return
			}
		}

		for _, key := range src.MapKeys() {
			srcElement := src.MapIndex(key)
			if !srcElement.IsValid() {
				continue
			}

			dstElement := dst.MapIndex(key)

			if dst.MapIndex(key).IsValid() {
				k := dstElement.Interface()
				dstElement = reflect.ValueOf(k)
			}

			if isReflectNil(srcElement) {
				if overwrite || isReflectNil(dstElement) {
					dst.SetMapIndex(key, srcElement)
				}

				continue
			}

			if !srcElement.CanInterface() {
				continue
			}

			if srcElement.CanInterface() {
				srcElement = reflect.ValueOf(srcElement.Interface())

				if dstElement.IsValid() {
					dstElement = reflect.ValueOf(dstElement.Interface())
				}
			}

			dstElement, err = deepMerge(dstElement, srcElement, visited, depth+1, config)
			if err != nil {
				return
			}

			dst.SetMapIndex(key, dstElement)
		}
	case reflect.Slice:
		newSlice := dst

		if (!isEmptyValue(src) || overwriteWithEmptySrc || overwriteSliceWithEmptySrc) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice {
			if typeCheck && src.Type() != dst.Type() {
				err = fmt.Errorf("cannot override two slices with different type (%s, %s)", src.Type(), dst.Type())
				return
			}

			newSlice = src
		} else if config.AppendSlice {
			if typeCheck && src.Type() != dst.Type() {
				err = fmt.Errorf("cannot append two slice with different type (%s, %s)", src.Type(), dst.Type())
				return
			}
			newSlice = reflect.AppendSlice(dst, src)
		} else if config.deepMergeSlice {
			if typeCheck && src.Type() != dst.Type() {
				err = fmt.Errorf("cannot deep merge two slice with different type (%s, %s)", src.Type(), dst.Type())
				return
			}

			if src.Len() > dst.Len() {
				newSlice = reflect.MakeSlice(dst.Type(), src.Len(), src.Len())

				for i := 0; i < dst.Len(); i++ {
					newSlice.Index(i).Set(dst.Index(i))
				}
			}

			for i := 0; i < src.Len(); i++ {
				srcElem := src.Index(i)
				dstElem := newSlice.Index(i)

				if srcElem.CanInterface() {
					srcElem = reflect.ValueOf(srcElem.Interface())
				}

				if dstElem.CanInterface() {
					dstElem = reflect.ValueOf(dstElem.Interface())
				}

				if newSlice.Index(i).IsZero() {
					newSlice.Index(i).Set(srcElem)
					continue
				}

				dstElem, err = deepMerge(dstElem, srcElem, visited, depth+1, config)

				if err != nil {
					return
				}

				newSlice.Index(i).Set(dstElem)
			}
		}

		if dst.CanSet() {
			dst.Set(newSlice)
		} else {
			dst = newSlice
		}
	case reflect.Ptr, reflect.Interface:
		if isReflectNil(src) {
			break
		}

		if dst.Kind() != reflect.Ptr && src.Type().AssignableTo(dst.Type()) {
			if dst.IsNil() || overwrite {
				if overwrite || isEmptyValue(dst) {
					if dst.CanSet() {
						dst.Set(src)
					} else {
						dst = src
					}
				}
			}

			break
		}

		if src.Kind() != reflect.Interface {
			if dst.IsNil() || (src.Kind() != reflect.Ptr && overwrite) {
				if dst.CanSet() && (overwrite || isEmptyValue(dst)) {
					dst.Set(src)
				}
			} else if src.Kind() == reflect.Ptr {
				if dst, err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil {
					return
				}
				dst = dst.Addr()
			} else if dst.Elem().Type() == src.Type() {
				if dst, err = deepMerge(dst.Elem(), src, visited, depth+1, config); err != nil {
					return
				}
			} else {
				err = ErrDifferentArgumentsTypes
				return
			}

			break
		}

		if dst.IsNil() || overwrite {
			if (overwrite || isEmptyValue(dst)) && (overwriteWithEmptySrc || !isEmptyValue(src)) {
				if dst.CanSet() {
					dst.Set(src)
				} else {
					dst = src
				}
			}
		} else if _, err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil {
			return
		}
	default:
		mustSet := (!isEmptyValue(src) || overwriteWithEmptySrc) && (overwrite || isEmptyValue(dst))
		if mustSet {
			if dst.CanSet() {
				dst.Set(src)
			} else {
				dst = src
			}
		}
	}

	return dst, nil
}

// Merge will fill any empty for value type attributes on the dst struct using corresponding
// src attributes if they themselves are not empty. dst and src must be valid same-type structs
// and dst must be a pointer to struct.
// It won't merge unexported (private) fields and will do recursively any exported field.
func Merge(dst, src interface{}, opts ...func(*Config)) error {
	return merge(dst, src, opts...)
}

// MergeWithOverwrite will do the same as Merge except that non-empty dst attributes will be overridden by
// non-empty src attribute values.
// Deprecated: use Merge(…) with WithOverride
func MergeWithOverwrite(dst, src interface{}, opts ...func(*Config)) error {
	return merge(dst, src, append(opts, WithOverride)...)
}

// WithTransformers adds transformers to merge, allowing to customize the merging of some types.
func WithTransformers(transformers Transformers) func(*Config) {
	return func(config *Config) {
		config.Transformers = transformers
	}
}

// WithOverride will make merge override non-empty dst attributes with non-empty src attributes values.
func WithOverride(config *Config) {
	config.Overwrite = true
}

// WithOverwriteWithEmptyValue will make merge override non empty dst attributes with empty src attributes values.
func WithOverwriteWithEmptyValue(config *Config) {
	config.Overwrite = true
	config.overwriteWithEmptyValue = true
}

// WithOverrideEmptySlice will make merge override empty dst slice with empty src slice.
func WithOverrideEmptySlice(config *Config) {
	config.overwriteSliceWithEmptyValue = true
}

// WithAppendSlice will make merge append slices instead of overwriting it.
func WithAppendSlice(config *Config) {
	config.AppendSlice = true
}

// WithTypeCheck will make merge check types while overwriting it (must be used with WithOverride).
func WithTypeCheck(config *Config) {
	config.TypeCheck = true
}

// WithDeepMergeSlice will make merge deep merge slice elements pairwise (resizing dst slice if needed)
func WithDeepMergeSlice(config *Config) {
	config.deepMergeSlice = true
}

func merge(dst, src interface{}, opts ...func(*Config)) error {
	if dst != nil && reflect.ValueOf(dst).Kind() != reflect.Ptr {
		return ErrNonPointerArgument
	}
	var (
		vDst, vSrc reflect.Value
		err        error
	)

	config := &Config{}

	for _, opt := range opts {
		opt(config)
	}

	if vDst, vSrc, err = resolveValues(dst, src); err != nil {
		return err
	}
	if !vDst.CanSet() {
		return fmt.Errorf("cannot set dst, needs reference")
	}
	if vDst.Type() != vSrc.Type() {
		return ErrDifferentArgumentsTypes
	}
	_, err = deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, config)
	return err
}

// IsReflectNil is the reflect value provided nil
func isReflectNil(v reflect.Value) bool {
	k := v.Kind()
	switch k {
	case reflect.Interface, reflect.Slice, reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr:
		// Both interface and slice are nil if first word is 0.
		// Both are always bigger than a word; assume flagIndir.
		return v.IsNil()
	default:
		return false
	}
}
<|endoftext|>