<commit_before>package pt

import "math"

// SDFShape

type SDFShape struct {
	SDF
	Material Material
}

func NewSDFShape(sdf SDF, material Material) Shape {
	return &SDFShape{sdf, material}
}

func (s *SDFShape) Compile() {
}

func (s *SDFShape) Intersect(ray Ray) Hit {
	const epsilon = 0.00001
	const start = 0.0001
	const jumpSize = 0.001
	box := s.BoundingBox()
	t1, t2 := box.Intersect(ray)
	if t2 < t1 || t2 < 0 {
		return NoHit
	}
	t := math.Max(start, t1)
	jump := true
	for i := 0; i < 1000; i++ {
		d := s.Evaluate(ray.Position(t))
		if jump && d < 0 {
			t -= jumpSize
			jump = false
			continue
		}
		if d < epsilon {
			return Hit{s, t, nil}
		}
		if jump && d < jumpSize {
			d = jumpSize
		}
		t += d
		if t > t2 {
			return NoHit
		}
	}
	return NoHit
}

func (s *SDFShape) UV(p Vector) Vector {
	return Vector{}
}

func (s *SDFShape) NormalAt(p Vector) Vector {
	const e = 0.0001
	x, y, z := p.X, p.Y, p.Z
	n := Vector{
		s.Evaluate(Vector{x - e, y, z}) - s.Evaluate(Vector{x + e, y, z}),
		s.Evaluate(Vector{x, y - e, z}) - s.Evaluate(Vector{x, y + e, z}),
		s.Evaluate(Vector{x, y, z - e}) - s.Evaluate(Vector{x, y, z + e}),
	}
	return n.Normalize()
}

func (s *SDFShape) MaterialAt(p Vector) Material {
	return s.Material
}

// SDF

type SDF interface {
	Evaluate(p Vector) float64
	BoundingBox() Box
}

// SphereSDF

type SphereSDF struct {
	Radius   float64
	Exponent float64
}

func NewSphereSDF(radius float64) SDF {
	return &SphereSDF{radius, 2}
}

func (s *SphereSDF) Evaluate(p Vector) float64 {
	return p.LengthN(s.Exponent) - s.Radius
}

func (s *SphereSDF) BoundingBox() Box {
	r := s.Radius
	return Box{Vector{-r, -r, -r}, Vector{r, r, r}}
}

// CubeSDF

type CubeSDF struct {
	Size Vector
}

func NewCubeSDF(size Vector) SDF {
	return &CubeSDF{size}
}

func (s *CubeSDF) Evaluate(p Vector) float64 {
	x := p.X
	y := p.Y
	z := p.Z
	if x < 0 {
		x = -x
	}
	if y < 0 {
		y = -y
	}
	if z < 0 {
		z = -z
	}
	x -= s.Size.X / 2
	y -= s.Size.Y / 2
	z -= s.Size.Z / 2
	a := x
	if y > a {
		a = y
	}
	if z > a {
		a = z
	}
	if a > 0 {
		a = 0
	}
	if x < 0 {
		x = 0
	}
	if y < 0 {
		y = 0
	}
	if z < 0 {
		z = 0
	}
	b := math.Sqrt(x*x + y*y + z*z)
	return a + b
}

func (s *CubeSDF) BoundingBox() Box {
	x, y, z := s.Size.X/2, s.Size.Y/2, s.Size.Z/2
	return Box{Vector{-x, -y, -z}, Vector{x, y, z}}
}

// CylinderSDF

type CylinderSDF struct {
	Radius float64
	Height float64
}

func NewCylinderSDF(radius, height float64) SDF {
	return &CylinderSDF{radius, height}
}

func (s *CylinderSDF) Evaluate(p Vector) float64 {
	x := math.Sqrt(p.X*p.X + p.Z*p.Z)
	y := p.Y
	if x < 0 {
		x = -x
	}
	if y < 0 {
		y = -y
	}
	x -= s.Radius
	y -= s.Height / 2
	a := x
	if y > a {
		a = y
	}
	if a > 0 {
		a = 0
	}
	if x < 0 {
		x = 0
	}
	if y < 0 {
		y = 0
	}
	b := math.Sqrt(x*x + y*y)
	return a + b
}

func (s *CylinderSDF) BoundingBox() Box {
	r := s.Radius
	h := s.Height / 2
	return Box{Vector{-r, -h, -r}, Vector{r, h, r}}
}

// CapsuleSDF

type CapsuleSDF struct {
	A, B     Vector
	Radius   float64
	Exponent float64
}

func NewCapsuleSDF(a, b Vector, radius float64) SDF {
	return &CapsuleSDF{a, b, radius, 2}
}

func (s *CapsuleSDF) Evaluate(p Vector) float64 {
	pa := p.Sub(s.A)
	ba := s.B.Sub(s.A)
	h := math.Max(0, math.Min(1, pa.Dot(ba)/ba.Dot(ba)))
	return pa.Sub(ba.MulScalar(h)).LengthN(s.Exponent) - s.Radius
}

func (s *CapsuleSDF) BoundingBox() Box {
	a, b := s.A.Min(s.B), s.A.Max(s.B)
	return Box{a.SubScalar(s.Radius), b.AddScalar(s.Radius)}
}

// TorusSDF

type TorusSDF struct {
	MajorRadius   float64
	MinorRadius   float64
	MajorExponent float64
	MinorExponent float64
}

func NewTorusSDF(major, minor float64) SDF {
	return &TorusSDF{major, minor, 2, 2}
}

func (s *TorusSDF) Evaluate(p Vector) float64 {
	q := Vector{Vector{p.X, p.Y, 0}.LengthN(s.MajorExponent) - s.MajorRadius, p.Z, 0}
	return q.LengthN(s.MinorExponent) - s.MinorRadius
}

func (s *TorusSDF) BoundingBox() Box {
	a := s.MinorRadius
	b := s.MinorRadius + s.MajorRadius
	return Box{Vector{-b, -b, -a}, Vector{b, b, a}}
}

// TransformSDF

type TransformSDF struct {
	SDF
	Matrix  Matrix
	Inverse Matrix
}

func NewTransformSDF(sdf SDF, matrix Matrix) SDF {
	return &TransformSDF{sdf, matrix, matrix.Inverse()}
}

func (s *TransformSDF) Evaluate(p Vector) float64 {
	q := s.Inverse.MulPosition(p)
	return s.SDF.Evaluate(q)
}

func (s *TransformSDF) BoundingBox() Box {
	return s.Matrix.MulBox(s.SDF.BoundingBox())
}

// ScaleSDF

type ScaleSDF struct {
	SDF
	Factor float64
}

func NewScaleSDF(sdf SDF, factor float64) SDF {
	return &ScaleSDF{sdf, factor}
}

func (s *ScaleSDF) Evaluate(p Vector) float64 {
	return s.SDF.Evaluate(p.DivScalar(s.Factor)) * s.Factor
}

func (s *ScaleSDF) BoundingBox() Box {
	f := s.Factor
	m := Scale(Vector{f, f, f})
	return m.MulBox(s.SDF.BoundingBox())
}

// UnionSDF

type UnionSDF struct {
	Items []SDF
}

func NewUnionSDF(items ...SDF) SDF {
	return &UnionSDF{items}
}

func (s *UnionSDF) Evaluate(p Vector) float64 {
	var result float64
	for i, item := range s.Items {
		d := item.Evaluate(p)
		if i == 0 || d < result {
			result = d
		}
	}
	return result
}

func (s *UnionSDF) BoundingBox() Box {
	var result Box
	for i, item := range s.Items {
		box := item.BoundingBox()
		if i == 0 {
			result = box
		} else {
			result = result.Extend(box)
		}
	}
	return result
}

// DifferenceSDF

type DifferenceSDF struct {
	Items []SDF
}

func NewDifferenceSDF(items ...SDF) SDF {
	return &DifferenceSDF{items}
}

func (s *DifferenceSDF) Evaluate(p Vector) float64 {
	var result float64
	for i, item := range s.Items {
		d := item.Evaluate(p)
		if i == 0 {
			result = d
		} else if -d > result {
			result = -d
		}
	}
	return result
}

func (s *DifferenceSDF) BoundingBox() Box {
	return s.Items[0].BoundingBox()
}

// IntersectionSDF

type IntersectionSDF struct {
	Items []SDF
}

func NewIntersectionSDF(items ...SDF) SDF {
	return &IntersectionSDF{items}
}

func (s *IntersectionSDF) Evaluate(p Vector) float64 {
	var result float64
	for i, item := range s.Items {
		d := item.Evaluate(p)
		if i == 0 || d > result {
			result = d
		}
	}
	return result
}

func (s *IntersectionSDF) BoundingBox() Box {
	// TODO: intersect boxes
	var result Box
	for i, item := range s.Items {
		box := item.BoundingBox()
		if i == 0 {
			result = box
		} else {
			result = result.Extend(box)
		}
	}
	return result
}
<commit_msg>RepeatSDF<commit_after>package pt

import "math"

// SDFShape

type SDFShape struct {
	SDF
	Material Material
}

func NewSDFShape(sdf SDF, material Material) Shape {
	return &SDFShape{sdf, material}
}

func (s *SDFShape) Compile() {
}

func (s *SDFShape) Intersect(ray Ray) Hit {
	const epsilon = 0.00001
	const start = 0.0001
	const jumpSize = 0.001
	box := s.BoundingBox()
	t1, t2 := box.Intersect(ray)
	if t2 < t1 || t2 < 0 {
		return NoHit
	}
	t := math.Max(start, t1)
	jump := true
	for i := 0; i < 1000; i++ {
		d := s.Evaluate(ray.Position(t))
		if jump && d < 0 {
			t -= jumpSize
			jump = false
			continue
		}
		if d < epsilon {
			return Hit{s, t, nil}
		}
		if jump && d < jumpSize {
			d = jumpSize
		}
		t += d
		if t > t2 {
			return NoHit
		}
	}
	return NoHit
}

func (s *SDFShape) UV(p Vector) Vector {
	return Vector{}
}

func (s *SDFShape) NormalAt(p Vector) Vector {
	const e = 0.0001
	x, y, z := p.X, p.Y, p.Z
	n := Vector{
		s.Evaluate(Vector{x - e, y, z}) - s.Evaluate(Vector{x + e, y, z}),
		s.Evaluate(Vector{x, y - e, z}) - s.Evaluate(Vector{x, y + e, z}),
		s.Evaluate(Vector{x, y, z - e}) - s.Evaluate(Vector{x, y, z + e}),
	}
	return n.Normalize()
}

func (s *SDFShape) MaterialAt(p Vector) Material {
	return s.Material
}

// SDF

type SDF interface {
	Evaluate(p Vector) float64
	BoundingBox() Box
}

// SphereSDF

type SphereSDF struct {
	Radius   float64
	Exponent float64
}

func NewSphereSDF(radius float64) SDF {
	return &SphereSDF{radius, 2}
}

func (s *SphereSDF) Evaluate(p Vector) float64 {
	return p.LengthN(s.Exponent) - s.Radius
}

func (s *SphereSDF) BoundingBox() Box {
	r := s.Radius
	return Box{Vector{-r, -r, -r}, Vector{r, r, r}}
}

// CubeSDF

type CubeSDF struct {
	Size Vector
}

func NewCubeSDF(size Vector) SDF {
	return &CubeSDF{size}
}

func (s *CubeSDF) Evaluate(p Vector) float64 {
	x := p.X
	y := p.Y
	z := p.Z
	if x < 0 {
		x = -x
	}
	if y < 0 {
		y = -y
	}
	if z < 0 {
		z = -z
	}
	x -= s.Size.X / 2
	y -= s.Size.Y / 2
	z -= s.Size.Z / 2
	a := x
	if y > a {
		a = y
	}
	if z > a {
		a = z
	}
	if a > 0 {
		a = 0
	}
	if x < 0 {
		x = 0
	}
	if y < 0 {
		y = 0
	}
	if z < 0 {
		z = 0
	}
	b := math.Sqrt(x*x + y*y + z*z)
	return a + b
}

func (s *CubeSDF) BoundingBox() Box {
	x, y, z := s.Size.X/2, s.Size.Y/2, s.Size.Z/2
	return Box{Vector{-x, -y, -z}, Vector{x, y, z}}
}

// CylinderSDF

type CylinderSDF struct {
	Radius float64
	Height float64
}

func NewCylinderSDF(radius, height float64) SDF {
	return &CylinderSDF{radius, height}
}

func (s *CylinderSDF) Evaluate(p Vector) float64 {
	x := math.Sqrt(p.X*p.X + p.Z*p.Z)
	y := p.Y
	if x < 0 {
		x = -x
	}
	if y < 0 {
		y = -y
	}
	x -= s.Radius
	y -= s.Height / 2
	a := x
	if y > a {
		a = y
	}
	if a > 0 {
		a = 0
	}
	if x < 0 {
		x = 0
	}
	if y < 0 {
		y = 0
	}
	b := math.Sqrt(x*x + y*y)
	return a + b
}

func (s *CylinderSDF) BoundingBox() Box {
	r := s.Radius
	h := s.Height / 2
	return Box{Vector{-r, -h, -r}, Vector{r, h, r}}
}

// CapsuleSDF

type CapsuleSDF struct {
	A, B     Vector
	Radius   float64
	Exponent float64
}

func NewCapsuleSDF(a, b Vector, radius float64) SDF {
	return &CapsuleSDF{a, b, radius, 2}
}

func (s *CapsuleSDF) Evaluate(p Vector) float64 {
	pa := p.Sub(s.A)
	ba := s.B.Sub(s.A)
	h := math.Max(0, math.Min(1, pa.Dot(ba)/ba.Dot(ba)))
	return pa.Sub(ba.MulScalar(h)).LengthN(s.Exponent) - s.Radius
}

func (s *CapsuleSDF) BoundingBox() Box {
	a, b := s.A.Min(s.B), s.A.Max(s.B)
	return Box{a.SubScalar(s.Radius), b.AddScalar(s.Radius)}
}

// TorusSDF

type TorusSDF struct {
	MajorRadius   float64
	MinorRadius   float64
	MajorExponent float64
	MinorExponent float64
}

func NewTorusSDF(major, minor float64) SDF {
	return &TorusSDF{major, minor, 2, 2}
}

func (s *TorusSDF) Evaluate(p Vector) float64 {
	q := Vector{Vector{p.X, p.Y, 0}.LengthN(s.MajorExponent) - s.MajorRadius, p.Z, 0}
	return q.LengthN(s.MinorExponent) - s.MinorRadius
}

func (s *TorusSDF) BoundingBox() Box {
	a := s.MinorRadius
	b := s.MinorRadius + s.MajorRadius
	return Box{Vector{-b, -b, -a}, Vector{b, b, a}}
}

// TransformSDF

type TransformSDF struct {
	SDF
	Matrix  Matrix
	Inverse Matrix
}

func NewTransformSDF(sdf SDF, matrix Matrix) SDF {
	return &TransformSDF{sdf, matrix, matrix.Inverse()}
}

func (s *TransformSDF) Evaluate(p Vector) float64 {
	q := s.Inverse.MulPosition(p)
	return s.SDF.Evaluate(q)
}

func (s *TransformSDF) BoundingBox() Box {
	return s.Matrix.MulBox(s.SDF.BoundingBox())
}

// ScaleSDF

type ScaleSDF struct {
	SDF
	Factor float64
}

func NewScaleSDF(sdf SDF, factor float64) SDF {
	return &ScaleSDF{sdf, factor}
}

func (s *ScaleSDF) Evaluate(p Vector) float64 {
	return s.SDF.Evaluate(p.DivScalar(s.Factor)) * s.Factor
}

func (s *ScaleSDF) BoundingBox() Box {
	f := s.Factor
	m := Scale(Vector{f, f, f})
	return m.MulBox(s.SDF.BoundingBox())
}

// UnionSDF

type UnionSDF struct {
	Items []SDF
}

func NewUnionSDF(items ...SDF) SDF {
	return &UnionSDF{items}
}

func (s *UnionSDF) Evaluate(p Vector) float64 {
	var result float64
	for i, item := range s.Items {
		d := item.Evaluate(p)
		if i == 0 || d < result {
			result = d
		}
	}
	return result
}

func (s *UnionSDF) BoundingBox() Box {
	var result Box
	for i, item := range s.Items {
		box := item.BoundingBox()
		if i == 0 {
			result = box
		} else {
			result = result.Extend(box)
		}
	}
	return result
}

// DifferenceSDF

type DifferenceSDF struct {
	Items []SDF
}

func NewDifferenceSDF(items ...SDF) SDF {
	return &DifferenceSDF{items}
}

func (s *DifferenceSDF) Evaluate(p Vector) float64 {
	var result float64
	for i, item := range s.Items {
		d := item.Evaluate(p)
		if i == 0 {
			result = d
		} else if -d > result {
			result = -d
		}
	}
	return result
}

func (s *DifferenceSDF) BoundingBox() Box {
	return s.Items[0].BoundingBox()
}

// IntersectionSDF

type IntersectionSDF struct {
	Items []SDF
}

func NewIntersectionSDF(items ...SDF) SDF {
	return &IntersectionSDF{items}
}

func (s *IntersectionSDF) Evaluate(p Vector) float64 {
	var result float64
	for i, item := range s.Items {
		d := item.Evaluate(p)
		if i == 0 || d > result {
			result = d
		}
	}
	return result
}

func (s *IntersectionSDF) BoundingBox() Box {
	// TODO: intersect boxes
	var result Box
	for i, item := range s.Items {
		box := item.BoundingBox()
		if i == 0 {
			result = box
		} else {
			result = result.Extend(box)
		}
	}
	return result
}

// RepeatSDF

type RepeatSDF struct {
	SDF
	Step Vector
}

func NewRepeatSDF(sdf SDF, step Vector) SDF {
	return &RepeatSDF{sdf, step}
}

func (s *RepeatSDF) Evaluate(p Vector) float64 {
	q := p.Mod(s.Step).Sub(s.Step.DivScalar(2))
	return s.SDF.Evaluate(q)
}

func (s *RepeatSDF) BoundingBox() Box {
	// TODO: fix this
	return Box{}
}
<|endoftext|>


<commit_before><commit_msg>assets<commit_after><|endoftext|>


<commit_before>package redis

import (
	"errors"
	"fmt"
	"strings"
	"sync"
	"time"

	"github.com/go-redis/redis/internal"
	"github.com/go-redis/redis/internal/pool"
	"github.com/go-redis/redis/internal/proto"
)

var errPingTimeout = errors.New("redis: ping timeout")

// PubSub implements Pub/Sub commands bas described in
// http://redis.io/topics/pubsub. Message receiving is NOT safe
// for concurrent use by multiple goroutines.
//
// PubSub automatically reconnects to Redis Server and resubscribes
// to the channels in case of network errors.
type PubSub struct {
	opt *Options

	newConn   func([]string) (*pool.Conn, error)
	closeConn func(*pool.Conn) error

	mu       sync.Mutex
	cn       *pool.Conn
	channels map[string]struct{}
	patterns map[string]struct{}

	closed bool
	exit   chan struct{}

	cmd *Cmd

	chOnce sync.Once
	ch     chan *Message
	ping   chan struct{}
}

func (c *PubSub) String() string {
	channels := mapKeys(c.channels)
	channels = append(channels, mapKeys(c.patterns)...)
	return fmt.Sprintf("PubSub(%s)", strings.Join(channels, ", "))
}

func (c *PubSub) init() {
	c.exit = make(chan struct{})
}

func (c *PubSub) conn() (*pool.Conn, error) {
	c.mu.Lock()
	cn, err := c._conn(nil)
	c.mu.Unlock()
	return cn, err
}

func (c *PubSub) _conn(newChannels []string) (*pool.Conn, error) {
	if c.closed {
		return nil, pool.ErrClosed
	}
	if c.cn != nil {
		return c.cn, nil
	}

	channels := mapKeys(c.channels)
	channels = append(channels, newChannels...)

	cn, err := c.newConn(channels)
	if err != nil {
		return nil, err
	}

	if err := c.resubscribe(cn); err != nil {
		_ = c.closeConn(cn)
		return nil, err
	}

	c.cn = cn
	return cn, nil
}

func (c *PubSub) writeCmd(cn *pool.Conn, cmd Cmder) error {
	return cn.WithWriter(c.opt.WriteTimeout, func(wr *proto.Writer) error {
		return writeCmd(wr, cmd)
	})
}

func (c *PubSub) resubscribe(cn *pool.Conn) error {
	var firstErr error

	if len(c.channels) > 0 {
		err := c._subscribe(cn, "subscribe", mapKeys(c.channels))
		if err != nil && firstErr == nil {
			firstErr = err
		}
	}

	if len(c.patterns) > 0 {
		err := c._subscribe(cn, "psubscribe", mapKeys(c.patterns))
		if err != nil && firstErr == nil {
			firstErr = err
		}
	}

	return firstErr
}

func mapKeys(m map[string]struct{}) []string {
	s := make([]string, len(m))
	i := 0
	for k := range m {
		s[i] = k
		i++
	}
	return s
}

func (c *PubSub) _subscribe(
	cn *pool.Conn, redisCmd string, channels []string,
) error {
	args := make([]interface{}, 0, 1+len(channels))
	args = append(args, redisCmd)
	for _, channel := range channels {
		args = append(args, channel)
	}
	cmd := NewSliceCmd(args...)
	return c.writeCmd(cn, cmd)
}

func (c *PubSub) releaseConn(cn *pool.Conn, err error, allowTimeout bool) {
	c.mu.Lock()
	c._releaseConn(cn, err, allowTimeout)
	c.mu.Unlock()
}

func (c *PubSub) _releaseConn(cn *pool.Conn, err error, allowTimeout bool) {
	if c.cn != cn {
		return
	}
	if internal.IsBadConn(err, allowTimeout) {
		c._reconnect(err)
	}
}

func (c *PubSub) _reconnect(reason error) {
	_ = c._closeTheCn(reason)
	_, _ = c._conn(nil)
}

func (c *PubSub) _closeTheCn(reason error) error {
	if c.cn == nil {
		return nil
	}
	if !c.closed {
		internal.Logf("redis: discarding bad PubSub connection: %s", reason)
	}
	err := c.closeConn(c.cn)
	c.cn = nil
	return err
}

func (c *PubSub) Close() error {
	c.mu.Lock()
	defer c.mu.Unlock()

	if c.closed {
		return pool.ErrClosed
	}
	c.closed = true
	close(c.exit)

	err := c._closeTheCn(pool.ErrClosed)
	return err
}

// Subscribe the client to the specified channels. It returns
// empty subscription if there are no channels.
func (c *PubSub) Subscribe(channels ...string) error {
	c.mu.Lock()
	defer c.mu.Unlock()

	err := c.subscribe("subscribe", channels...)
	if c.channels == nil {
		c.channels = make(map[string]struct{})
	}
	for _, s := range channels {
		c.channels[s] = struct{}{}
	}
	return err
}

// PSubscribe the client to the given patterns. It returns
// empty subscription if there are no patterns.
func (c *PubSub) PSubscribe(patterns ...string) error {
	c.mu.Lock()
	defer c.mu.Unlock()

	err := c.subscribe("psubscribe", patterns...)
	if c.patterns == nil {
		c.patterns = make(map[string]struct{})
	}
	for _, s := range patterns {
		c.patterns[s] = struct{}{}
	}
	return err
}

// Unsubscribe the client from the given channels, or from all of
// them if none is given.
func (c *PubSub) Unsubscribe(channels ...string) error {
	c.mu.Lock()
	defer c.mu.Unlock()

	for _, channel := range channels {
		delete(c.channels, channel)
	}
	err := c.subscribe("unsubscribe", channels...)
	return err
}

// PUnsubscribe the client from the given patterns, or from all of
// them if none is given.
func (c *PubSub) PUnsubscribe(patterns ...string) error {
	c.mu.Lock()
	defer c.mu.Unlock()

	for _, pattern := range patterns {
		delete(c.patterns, pattern)
	}
	err := c.subscribe("punsubscribe", patterns...)
	return err
}

func (c *PubSub) subscribe(redisCmd string, channels ...string) error {
	cn, err := c._conn(channels)
	if err != nil {
		return err
	}

	err = c._subscribe(cn, redisCmd, channels)
	c._releaseConn(cn, err, false)
	return err
}

func (c *PubSub) Ping(payload ...string) error {
	args := []interface{}{"ping"}
	if len(payload) == 1 {
		args = append(args, payload[0])
	}
	cmd := NewCmd(args...)

	cn, err := c.conn()
	if err != nil {
		return err
	}

	err = c.writeCmd(cn, cmd)
	c.releaseConn(cn, err, false)
	return err
}

// Subscription received after a successful subscription to channel.
type Subscription struct {
	// Can be "subscribe", "unsubscribe", "psubscribe" or "punsubscribe".
	Kind string
	// Channel name we have subscribed to.
	Channel string
	// Number of channels we are currently subscribed to.
	Count int
}

func (m *Subscription) String() string {
	return fmt.Sprintf("%s: %s", m.Kind, m.Channel)
}

// Message received as result of a PUBLISH command issued by another client.
type Message struct {
	Channel string
	Pattern string
	Payload string
}

func (m *Message) String() string {
	return fmt.Sprintf("Message<%s: %s>", m.Channel, m.Payload)
}

// Pong received as result of a PING command issued by another client.
type Pong struct {
	Payload string
}

func (p *Pong) String() string {
	if p.Payload != "" {
		return fmt.Sprintf("Pong<%s>", p.Payload)
	}
	return "Pong"
}

func (c *PubSub) newMessage(reply interface{}) (interface{}, error) {
	switch reply := reply.(type) {
	case string:
		return &Pong{
			Payload: reply,
		}, nil
	case []interface{}:
		switch kind := reply[0].(string); kind {
		case "subscribe", "unsubscribe", "psubscribe", "punsubscribe":
			return &Subscription{
				Kind:    kind,
				Channel: reply[1].(string),
				Count:   int(reply[2].(int64)),
			}, nil
		case "message":
			return &Message{
				Channel: reply[1].(string),
				Payload: reply[2].(string),
			}, nil
		case "pmessage":
			return &Message{
				Pattern: reply[1].(string),
				Channel: reply[2].(string),
				Payload: reply[3].(string),
			}, nil
		case "pong":
			return &Pong{
				Payload: reply[1].(string),
			}, nil
		default:
			return nil, fmt.Errorf("redis: unsupported pubsub message: %q", kind)
		}
	default:
		return nil, fmt.Errorf("redis: unsupported pubsub message: %#v", reply)
	}
}

// ReceiveTimeout acts like Receive but returns an error if message
// is not received in time. This is low-level API and in most cases
// Channel should be used instead.
func (c *PubSub) ReceiveTimeout(timeout time.Duration) (interface{}, error) {
	if c.cmd == nil {
		c.cmd = NewCmd()
	}

	cn, err := c.conn()
	if err != nil {
		return nil, err
	}

	err = cn.WithReader(timeout, func(rd *proto.Reader) error {
		return c.cmd.readReply(rd)
	})

	c.releaseConn(cn, err, timeout > 0)
	if err != nil {
		return nil, err
	}

	return c.newMessage(c.cmd.Val())
}

// Receive returns a message as a Subscription, Message, Pong or error.
// See PubSub example for details. This is low-level API and in most cases
// Channel should be used instead.
func (c *PubSub) Receive() (interface{}, error) {
	return c.ReceiveTimeout(0)
}

// ReceiveMessage returns a Message or error ignoring Subscription and Pong
// messages. This is low-level API and in most cases Channel should be used
// instead.
func (c *PubSub) ReceiveMessage() (*Message, error) {
	for {
		msg, err := c.Receive()
		if err != nil {
			return nil, err
		}

		switch msg := msg.(type) {
		case *Subscription:
			// Ignore.
		case *Pong:
			// Ignore.
		case *Message:
			return msg, nil
		default:
			err := fmt.Errorf("redis: unknown message: %T", msg)
			return nil, err
		}
	}
}

// Channel returns a Go channel for concurrently receiving messages.
// It periodically sends Ping messages to test connection health.
// The channel is closed with PubSub. Receive* APIs can not be used
// after channel is created.
//
// If the Go channel is full for 30 seconds the message is dropped.
func (c *PubSub) Channel() <-chan *Message {
	return c.channel(100)
}

// ChannelSize is like Channel, but creates a Go channel
// with specified buffer size.
func (c *PubSub) ChannelSize(size int) <-chan *Message {
	return c.channel(size)
}

func (c *PubSub) channel(size int) <-chan *Message {
	c.chOnce.Do(func() {
		c.initChannel(size)
	})
	if cap(c.ch) != size {
		err := fmt.Errorf("redis: PubSub.Channel is called with different buffer size")
		panic(err)
	}
	return c.ch
}

func (c *PubSub) initChannel(size int) {
	const timeout = 30 * time.Second

	c.ch = make(chan *Message, size)
	c.ping = make(chan struct{}, 1)

	go func() {
		timer := time.NewTimer(timeout)
		timer.Stop()

		var errCount int
		for {
			msg, err := c.Receive()
			if err != nil {
				if err == pool.ErrClosed {
					close(c.ch)
					return
				}
				if errCount > 0 {
					time.Sleep(c.retryBackoff(errCount))
				}
				errCount++
				continue
			}

			errCount = 0

			// Any message is as good as a ping.
			select {
			case c.ping <- struct{}{}:
			default:
			}

			switch msg := msg.(type) {
			case *Subscription:
				// Ignore.
			case *Pong:
				// Ignore.
			case *Message:
				timer.Reset(timeout)
				select {
				case c.ch <- msg:
					if !timer.Stop() {
						<-timer.C
					}
				case <-timer.C:
					internal.Logf(
						"redis: %s channel is full for %s (message is dropped)",
						c, timeout)
				}
			default:
				internal.Logf("redis: unknown message type: %T", msg)
			}
		}
	}()

	go func() {
		timer := time.NewTimer(timeout)
		timer.Stop()

		healthy := true
		for {
			timer.Reset(timeout)
			select {
			case <-c.ping:
				healthy = true
				if !timer.Stop() {
					<-timer.C
				}
			case <-timer.C:
				pingErr := c.Ping()
				if healthy {
					healthy = false
				} else {
					if pingErr == nil {
						pingErr = errPingTimeout
					}
					c.mu.Lock()
					c._reconnect(pingErr)
					c.mu.Unlock()
				}
			case <-c.exit:
				return
			}
		}
	}()
}

func (c *PubSub) retryBackoff(attempt int) time.Duration {
	return internal.RetryBackoff(attempt, c.opt.MinRetryBackoff, c.opt.MaxRetryBackoff)
}
<commit_msg>Typo error in comments<commit_after>package redis

import (
	"errors"
	"fmt"
	"strings"
	"sync"
	"time"

	"github.com/go-redis/redis/internal"
	"github.com/go-redis/redis/internal/pool"
	"github.com/go-redis/redis/internal/proto"
)

var errPingTimeout = errors.New("redis: ping timeout")

// PubSub implements Pub/Sub commands as described in
// http://redis.io/topics/pubsub. Message receiving is NOT safe
// for concurrent use by multiple goroutines.
//
// PubSub automatically reconnects to Redis Server and resubscribes
// to the channels in case of network errors.
type PubSub struct {
	opt *Options

	newConn   func([]string) (*pool.Conn, error)
	closeConn func(*pool.Conn) error

	mu       sync.Mutex
	cn       *pool.Conn
	channels map[string]struct{}
	patterns map[string]struct{}

	closed bool
	exit   chan struct{}

	cmd *Cmd

	chOnce sync.Once
	ch     chan *Message
	ping   chan struct{}
}

func (c *PubSub) String() string {
	channels := mapKeys(c.channels)
	channels = append(channels, mapKeys(c.patterns)...)
	return fmt.Sprintf("PubSub(%s)", strings.Join(channels, ", "))
}

func (c *PubSub) init() {
	c.exit = make(chan struct{})
}

func (c *PubSub) conn() (*pool.Conn, error) {
	c.mu.Lock()
	cn, err := c._conn(nil)
	c.mu.Unlock()
	return cn, err
}

func (c *PubSub) _conn(newChannels []string) (*pool.Conn, error) {
	if c.closed {
		return nil, pool.ErrClosed
	}
	if c.cn != nil {
		return c.cn, nil
	}

	channels := mapKeys(c.channels)
	channels = append(channels, newChannels...)

	cn, err := c.newConn(channels)
	if err != nil {
		return nil, err
	}

	if err := c.resubscribe(cn); err != nil {
		_ = c.closeConn(cn)
		return nil, err
	}

	c.cn = cn
	return cn, nil
}

func (c *PubSub) writeCmd(cn *pool.Conn, cmd Cmder) error {
	return cn.WithWriter(c.opt.WriteTimeout, func(wr *proto.Writer) error {
		return writeCmd(wr, cmd)
	})
}

func (c *PubSub) resubscribe(cn *pool.Conn) error {
	var firstErr error

	if len(c.channels) > 0 {
		err := c._subscribe(cn, "subscribe", mapKeys(c.channels))
		if err != nil && firstErr == nil {
			firstErr = err
		}
	}

	if len(c.patterns) > 0 {
		err := c._subscribe(cn, "psubscribe", mapKeys(c.patterns))
		if err != nil && firstErr == nil {
			firstErr = err
		}
	}

	return firstErr
}

func mapKeys(m map[string]struct{}) []string {
	s := make([]string, len(m))
	i := 0
	for k := range m {
		s[i] = k
		i++
	}
	return s
}

func (c *PubSub) _subscribe(
	cn *pool.Conn, redisCmd string, channels []string,
) error {
	args := make([]interface{}, 0, 1+len(channels))
	args = append(args, redisCmd)
	for _, channel := range channels {
		args = append(args, channel)
	}
	cmd := NewSliceCmd(args...)
	return c.writeCmd(cn, cmd)
}

func (c *PubSub) releaseConn(cn *pool.Conn, err error, allowTimeout bool) {
	c.mu.Lock()
	c._releaseConn(cn, err, allowTimeout)
	c.mu.Unlock()
}

func (c *PubSub) _releaseConn(cn *pool.Conn, err error, allowTimeout bool) {
	if c.cn != cn {
		return
	}
	if internal.IsBadConn(err, allowTimeout) {
		c._reconnect(err)
	}
}

func (c *PubSub) _reconnect(reason error) {
	_ = c._closeTheCn(reason)
	_, _ = c._conn(nil)
}

func (c *PubSub) _closeTheCn(reason error) error {
	if c.cn == nil {
		return nil
	}
	if !c.closed {
		internal.Logf("redis: discarding bad PubSub connection: %s", reason)
	}
	err := c.closeConn(c.cn)
	c.cn = nil
	return err
}

func (c *PubSub) Close() error {
	c.mu.Lock()
	defer c.mu.Unlock()

	if c.closed {
		return pool.ErrClosed
	}
	c.closed = true
	close(c.exit)

	err := c._closeTheCn(pool.ErrClosed)
	return err
}

// Subscribe the client to the specified channels. It returns
// empty subscription if there are no channels.
func (c *PubSub) Subscribe(channels ...string) error {
	c.mu.Lock()
	defer c.mu.Unlock()

	err := c.subscribe("subscribe", channels...)
	if c.channels == nil {
		c.channels = make(map[string]struct{})
	}
	for _, s := range channels {
		c.channels[s] = struct{}{}
	}
	return err
}

// PSubscribe the client to the given patterns. It returns
// empty subscription if there are no patterns.
func (c *PubSub) PSubscribe(patterns ...string) error {
	c.mu.Lock()
	defer c.mu.Unlock()

	err := c.subscribe("psubscribe", patterns...)
	if c.patterns == nil {
		c.patterns = make(map[string]struct{})
	}
	for _, s := range patterns {
		c.patterns[s] = struct{}{}
	}
	return err
}

// Unsubscribe the client from the given channels, or from all of
// them if none is given.
func (c *PubSub) Unsubscribe(channels ...string) error {
	c.mu.Lock()
	defer c.mu.Unlock()

	for _, channel := range channels {
		delete(c.channels, channel)
	}
	err := c.subscribe("unsubscribe", channels...)
	return err
}

// PUnsubscribe the client from the given patterns, or from all of
// them if none is given.
func (c *PubSub) PUnsubscribe(patterns ...string) error {
	c.mu.Lock()
	defer c.mu.Unlock()

	for _, pattern := range patterns {
		delete(c.patterns, pattern)
	}
	err := c.subscribe("punsubscribe", patterns...)
	return err
}

func (c *PubSub) subscribe(redisCmd string, channels ...string) error {
	cn, err := c._conn(channels)
	if err != nil {
		return err
	}

	err = c._subscribe(cn, redisCmd, channels)
	c._releaseConn(cn, err, false)
	return err
}

func (c *PubSub) Ping(payload ...string) error {
	args := []interface{}{"ping"}
	if len(payload) == 1 {
		args = append(args, payload[0])
	}
	cmd := NewCmd(args...)

	cn, err := c.conn()
	if err != nil {
		return err
	}

	err = c.writeCmd(cn, cmd)
	c.releaseConn(cn, err, false)
	return err
}

// Subscription received after a successful subscription to channel.
type Subscription struct {
	// Can be "subscribe", "unsubscribe", "psubscribe" or "punsubscribe".
	Kind string
	// Channel name we have subscribed to.
	Channel string
	// Number of channels we are currently subscribed to.
	Count int
}

func (m *Subscription) String() string {
	return fmt.Sprintf("%s: %s", m.Kind, m.Channel)
}

// Message received as result of a PUBLISH command issued by another client.
type Message struct {
	Channel string
	Pattern string
	Payload string
}

func (m *Message) String() string {
	return fmt.Sprintf("Message<%s: %s>", m.Channel, m.Payload)
}

// Pong received as result of a PING command issued by another client.
type Pong struct {
	Payload string
}

func (p *Pong) String() string {
	if p.Payload != "" {
		return fmt.Sprintf("Pong<%s>", p.Payload)
	}
	return "Pong"
}

func (c *PubSub) newMessage(reply interface{}) (interface{}, error) {
	switch reply := reply.(type) {
	case string:
		return &Pong{
			Payload: reply,
		}, nil
	case []interface{}:
		switch kind := reply[0].(string); kind {
		case "subscribe", "unsubscribe", "psubscribe", "punsubscribe":
			return &Subscription{
				Kind:    kind,
				Channel: reply[1].(string),
				Count:   int(reply[2].(int64)),
			}, nil
		case "message":
			return &Message{
				Channel: reply[1].(string),
				Payload: reply[2].(string),
			}, nil
		case "pmessage":
			return &Message{
				Pattern: reply[1].(string),
				Channel: reply[2].(string),
				Payload: reply[3].(string),
			}, nil
		case "pong":
			return &Pong{
				Payload: reply[1].(string),
			}, nil
		default:
			return nil, fmt.Errorf("redis: unsupported pubsub message: %q", kind)
		}
	default:
		return nil, fmt.Errorf("redis: unsupported pubsub message: %#v", reply)
	}
}

// ReceiveTimeout acts like Receive but returns an error if message
// is not received in time. This is low-level API and in most cases
// Channel should be used instead.
func (c *PubSub) ReceiveTimeout(timeout time.Duration) (interface{}, error) {
	if c.cmd == nil {
		c.cmd = NewCmd()
	}

	cn, err := c.conn()
	if err != nil {
		return nil, err
	}

	err = cn.WithReader(timeout, func(rd *proto.Reader) error {
		return c.cmd.readReply(rd)
	})

	c.releaseConn(cn, err, timeout > 0)
	if err != nil {
		return nil, err
	}

	return c.newMessage(c.cmd.Val())
}

// Receive returns a message as a Subscription, Message, Pong or error.
// See PubSub example for details. This is low-level API and in most cases
// Channel should be used instead.
func (c *PubSub) Receive() (interface{}, error) {
	return c.ReceiveTimeout(0)
}

// ReceiveMessage returns a Message or error ignoring Subscription and Pong
// messages. This is low-level API and in most cases Channel should be used
// instead.
func (c *PubSub) ReceiveMessage() (*Message, error) {
	for {
		msg, err := c.Receive()
		if err != nil {
			return nil, err
		}

		switch msg := msg.(type) {
		case *Subscription:
			// Ignore.
		case *Pong:
			// Ignore.
		case *Message:
			return msg, nil
		default:
			err := fmt.Errorf("redis: unknown message: %T", msg)
			return nil, err
		}
	}
}

// Channel returns a Go channel for concurrently receiving messages.
// It periodically sends Ping messages to test connection health.
// The channel is closed with PubSub. Receive* APIs can not be used
// after channel is created.
//
// If the Go channel is full for 30 seconds the message is dropped.
func (c *PubSub) Channel() <-chan *Message {
	return c.channel(100)
}

// ChannelSize is like Channel, but creates a Go channel
// with specified buffer size.
func (c *PubSub) ChannelSize(size int) <-chan *Message {
	return c.channel(size)
}

func (c *PubSub) channel(size int) <-chan *Message {
	c.chOnce.Do(func() {
		c.initChannel(size)
	})
	if cap(c.ch) != size {
		err := fmt.Errorf("redis: PubSub.Channel is called with different buffer size")
		panic(err)
	}
	return c.ch
}

func (c *PubSub) initChannel(size int) {
	const timeout = 30 * time.Second

	c.ch = make(chan *Message, size)
	c.ping = make(chan struct{}, 1)

	go func() {
		timer := time.NewTimer(timeout)
		timer.Stop()

		var errCount int
		for {
			msg, err := c.Receive()
			if err != nil {
				if err == pool.ErrClosed {
					close(c.ch)
					return
				}
				if errCount > 0 {
					time.Sleep(c.retryBackoff(errCount))
				}
				errCount++
				continue
			}

			errCount = 0

			// Any message is as good as a ping.
			select {
			case c.ping <- struct{}{}:
			default:
			}

			switch msg := msg.(type) {
			case *Subscription:
				// Ignore.
			case *Pong:
				// Ignore.
			case *Message:
				timer.Reset(timeout)
				select {
				case c.ch <- msg:
					if !timer.Stop() {
						<-timer.C
					}
				case <-timer.C:
					internal.Logf(
						"redis: %s channel is full for %s (message is dropped)",
						c, timeout)
				}
			default:
				internal.Logf("redis: unknown message type: %T", msg)
			}
		}
	}()

	go func() {
		timer := time.NewTimer(timeout)
		timer.Stop()

		healthy := true
		for {
			timer.Reset(timeout)
			select {
			case <-c.ping:
				healthy = true
				if !timer.Stop() {
					<-timer.C
				}
			case <-timer.C:
				pingErr := c.Ping()
				if healthy {
					healthy = false
				} else {
					if pingErr == nil {
						pingErr = errPingTimeout
					}
					c.mu.Lock()
					c._reconnect(pingErr)
					c.mu.Unlock()
				}
			case <-c.exit:
				return
			}
		}
	}()
}

func (c *PubSub) retryBackoff(attempt int) time.Duration {
	return internal.RetryBackoff(attempt, c.opt.MinRetryBackoff, c.opt.MaxRetryBackoff)
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Tim Heckman
// Use of this source code is governed by the MIT License
// that can be found in the LICENSE.txt file.

package semaphore

import (
	"errors"
	"io"
)

// ErrUnusable is the error returned if the semaphore isn't suitable for use,
// meaning something called the Close() method. If this error is returned, the
// semaphore can no longer issue permits and the New() function must be used to
// allocate a new one.
var ErrUnusable = errors.New("semaphore not suitable for use, please create one with New()")

// ErrAlreadyClosed is the error returned from the Close() function if the
// semaphore has already been shut down. This error should be treated as an
// advisory and NOT a failure.
var ErrAlreadyClosed = errors.New("the semaphore has already had the Close() method called")

// Acquirer is the interface for taking permits from a semaphore.
type Acquirer interface {
	// Acquire is a blocking function to take a permit from the semaphore. If the
	// error returned is nil, the semaphore permit has been acquired and the work
	// can start.
	//
	// If an error is returned, the permit was not given and the work MUST NOT
	// start. This returns ErrUnusable if Close() has been called on the semaphore.
	// In this case you'd need to use New() to obtain a new usable semaphore.
	Acquire() error
}

// Releaser is the interface for releasing permits back to a semaphore.
type Releaser interface {
	// Release is a non-blocking function to release the semaphore. If an error is returned
	// from this function, the release was successful but the semaphore cannot be
	// used to acquire another permit.
	//
	// In other words, if this returns an error it should not be treated as a
	// failure. For example, if Close() is called, followed by Release(), this
	// function would return an ErrUnusable error. In this case you'd need to
	// use New() to obtain a new usable semaphore.
	Release() error
}

// Semaphore is the interface needed to implement a functioning semaphore. One
// function to take permit, one to give back a permit, and a final function to
// tell the semaphore to stop issuing permits.
type Semaphore interface {
	// Acquirer is for taking a semaphore permit. See its comments for more
	// details.
	Acquirer

	// Releaser is for releasing a semaphore permit. See its comments for more
	// details.
	Releaser

	// Close is a non-blocking function that shuts the semaphore down and
	// prevents it from issuing further permits. Consumers would need to create
	// a completely new semaphore, using New(), after calling Close() if they
	// wanted to request another permit.
	//
	// Close terminates the semaphore in a way that ensures any consumer with a
	// permit is able to release that permit without fatal error or deadlock.
	//
	// This function should return an ErrAlreadyClosed error if the semaphore
	// has already been closed, but consumers should treat that as an advisory
	// and not a fatal error.
	io.Closer
}

// New returns a new Semaphore and it takes a size argument to define how many
// concurrent permits the semaphore will issue. The size argument must be
// greater than 0 or an error will be returned.
func New(size int) (Semaphore, error) {
	if size < 1 {
		return nil, errors.New("size argument must be greater than 0")
	}

	return &semaphore{c: make(chan struct{}, size)}, nil
}

type semaphore struct {
	c chan struct{}
}

func (s *semaphore) Acquire() (err error) {
	defer func() {
		// this should catch panics for writing to a closed channel
		if r := recover(); r != nil {
			err = ErrUnusable
		}
	}()

	s.c <- struct{}{}

	return
}

func (s *semaphore) Release() error {
	if _, ok := <-s.c; !ok {
		return ErrUnusable
	}

	return nil
}

func (s *semaphore) Close() (err error) {
	// XXX(theckman): catch panic if semaphore channel already closed,
	// return ErrAlreadyClosed if so
	defer func() {
		if r := recover(); r != nil {
			err = ErrAlreadyClosed
		}
	}()

	close(s.c)

	return
}
<commit_msg>Add our own Closer interface that is only io.Closer<commit_after>// Copyright (c) 2017 Tim Heckman
// Use of this source code is governed by the MIT License
// that can be found in the LICENSE.txt file.

package semaphore

import (
	"errors"
	"io"
)

// ErrUnusable is the error returned if the semaphore isn't suitable for use,
// meaning something called the Close() method. If this error is returned, the
// semaphore can no longer issue permits and the New() function must be used to
// allocate a new one.
var ErrUnusable = errors.New("semaphore not suitable for use, please create one with New()")

// ErrAlreadyClosed is the error returned from the Close() function if the
// semaphore has already been shut down. This error should be treated as an
// advisory and NOT a failure.
var ErrAlreadyClosed = errors.New("the semaphore has already had the Close() method called")

// Acquirer is the interface for taking permits from a semaphore.
type Acquirer interface {
	// Acquire is a blocking function to take a permit from the semaphore. If the
	// error returned is nil, the semaphore permit has been acquired and the work
	// can start.
	//
	// If an error is returned, the permit was not given and the work MUST NOT
	// start. This returns ErrUnusable if Close() has been called on the semaphore.
	// In this case you'd need to use New() to obtain a new usable semaphore.
	Acquire() error
}

// Releaser is the interface for releasing permits back to a semaphore.
type Releaser interface {
	// Release is a non-blocking function to release the semaphore. If an error is returned
	// from this function, the release was successful but the semaphore cannot be
	// used to acquire another permit.
	//
	// In other words, if this returns an error it should not be treated as a
	// failure. For example, if Close() is called, followed by Release(), this
	// function would return an ErrUnusable error. In this case you'd need to
	// use New() to obtain a new usable semaphore.
	Release() error
}

// Closer is the interface for closing an interface to mark it as no longer in
// use.
type Closer interface {
	// Close is a non-blocking function that shuts the semaphore down and
	// prevents it from issuing further permits. Consumers would need to create
	// a completely new semaphore, using New(), after calling Close() if they
	// wanted to request another permit.
	//
	// Close terminates the semaphore in a way that ensures any consumer with a
	// permit is able to release that permit without fatal error or deadlock.
	//
	// This function should return an ErrAlreadyClosed error if the semaphore
	// has already been closed, but consumers should treat that as an advisory
	// and not a fatal error.
	io.Closer
}

// Semaphore is the interface needed to implement a functioning semaphore. One
// function to take permit, one to give back a permit, and a final function to
// tell the semaphore to stop issuing permits.
type Semaphore interface {
	// Acquirer is for taking a semaphore permit. See its comments for more
	// details.
	Acquirer

	// Releaser is for releasing a semaphore permit. See its comments for more
	// details.
	Releaser

	// Closer is for closing the semaphore. See its comments for more details.
	Closer
}

// New returns a new Semaphore and it takes a size argument to define how many
// concurrent permits the semaphore will issue. The size argument must be
// greater than 0 or an error will be returned.
func New(size int) (Semaphore, error) {
	if size < 1 {
		return nil, errors.New("size argument must be greater than 0")
	}

	return &semaphore{c: make(chan struct{}, size)}, nil
}

type semaphore struct {
	c chan struct{}
}

func (s *semaphore) Acquire() (err error) {
	defer func() {
		// this should catch panics for writing to a closed channel
		if r := recover(); r != nil {
			err = ErrUnusable
		}
	}()

	s.c <- struct{}{}

	return
}

func (s *semaphore) Release() error {
	if _, ok := <-s.c; !ok {
		return ErrUnusable
	}

	return nil
}

func (s *semaphore) Close() (err error) {
	// XXX(theckman): catch panic if semaphore channel already closed,
	// return ErrAlreadyClosed if so
	defer func() {
		if r := recover(); r != nil {
			err = ErrAlreadyClosed
		}
	}()

	close(s.c)

	return
}
<|endoftext|>


<commit_before>package gapi

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"os"
)

type DashboardMeta struct {
	IsStarred bool   `json:"isStarred"`
	Slug      string `json:"slug"`
	Folder    int64  `json:"folderId"`
}

type DashboardSaveResponse struct {
	Slug    string `json:"slug"`
	Id      int64  `json:"id"`
	Uid     string `json:"uid"`
	Status  string `json:"status"`
	Version int64  `json:"version"`
}

type Dashboard struct {
	Meta      DashboardMeta          `json:"meta"`
	Model     map[string]interface{} `json:"dashboard"`
	Folder    int64                  `json:"folderId"`
	Overwrite bool                   `json:overwrite`
}

// Deprecated: use NewDashboard instead
func (c *Client) SaveDashboard(model map[string]interface{}, overwrite bool) (*DashboardSaveResponse, error) {
	wrapper := map[string]interface{}{
		"dashboard": model,
		"overwrite": overwrite,
	}
	data, err := json.Marshal(wrapper)
	if err != nil {
		return nil, err
	}
	req, err := c.newRequest("POST", "/api/dashboards/db", nil, bytes.NewBuffer(data))
	if err != nil {
		return nil, err
	}

	resp, err := c.Do(req)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != 200 {
		data, _ = ioutil.ReadAll(resp.Body)
		return nil, fmt.Errorf("status: %d, body: %s", resp.StatusCode, data)
	}

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

	result := &DashboardSaveResponse{}
	err = json.Unmarshal(data, &result)
	return result, err
}

func (c *Client) NewDashboard(dashboard Dashboard) (*DashboardSaveResponse, error) {
	data, err := json.Marshal(dashboard)
	if err != nil {
		return nil, err
	}
	req, err := c.newRequest("POST", "/api/dashboards/db", nil, bytes.NewBuffer(data))
	if err != nil {
		return nil, err
	}

	resp, err := c.Do(req)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != 200 {
		return nil, errors.New(resp.Status)
	}

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

	result := &DashboardSaveResponse{}
	err = json.Unmarshal(data, &result)
	return result, err
}

func (c *Client) Dashboard(slug string) (*Dashboard, error) {
	path := fmt.Sprintf("/api/dashboards/db/%s", slug)
	req, err := c.newRequest("GET", path, nil, nil)
	if err != nil {
		return nil, err
	}

	resp, err := c.Do(req)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != 200 {
		return nil, errors.New(resp.Status)
	}

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

	result := &Dashboard{}
	err = json.Unmarshal(data, &result)
	result.Folder = result.Meta.Folder
	if os.Getenv("GF_LOG") != "" {
		log.Printf("got back dashboard response  %s", data)
	}
	return result, err
}

func (c *Client) DeleteDashboard(slug string) error {
	path := fmt.Sprintf("/api/dashboards/db/%s", slug)
	req, err := c.newRequest("DELETE", path, nil, nil)
	if err != nil {
		return err
	}

	resp, err := c.Do(req)
	if err != nil {
		return err
	}
	if resp.StatusCode != 200 {
		return errors.New(resp.Status)
	}

	return nil
}
<commit_msg>Implement read & delete by UID as per Grafana depreciating (#25)<commit_after>package gapi

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"os"
)

type DashboardMeta struct {
	IsStarred bool   `json:"isStarred"`
	Slug      string `json:"slug"`
	Folder    int64  `json:"folderId"`
}

type DashboardSaveResponse struct {
	Slug    string `json:"slug"`
	Id      int64  `json:"id"`
	Uid     string `json:"uid"`
	Status  string `json:"status"`
	Version int64  `json:"version"`
}

type Dashboard struct {
	Meta      DashboardMeta          `json:"meta"`
	Model     map[string]interface{} `json:"dashboard"`
	Folder    int64                  `json:"folderId"`
	Overwrite bool                   `json:overwrite`
}

// Deprecated: use NewDashboard instead
func (c *Client) SaveDashboard(model map[string]interface{}, overwrite bool) (*DashboardSaveResponse, error) {
	wrapper := map[string]interface{}{
		"dashboard": model,
		"overwrite": overwrite,
	}
	data, err := json.Marshal(wrapper)
	if err != nil {
		return nil, err
	}
	req, err := c.newRequest("POST", "/api/dashboards/db", nil, bytes.NewBuffer(data))
	if err != nil {
		return nil, err
	}

	resp, err := c.Do(req)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != 200 {
		data, _ = ioutil.ReadAll(resp.Body)
		return nil, fmt.Errorf("status: %d, body: %s", resp.StatusCode, data)
	}

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

	result := &DashboardSaveResponse{}
	err = json.Unmarshal(data, &result)
	return result, err
}

func (c *Client) NewDashboard(dashboard Dashboard) (*DashboardSaveResponse, error) {
	data, err := json.Marshal(dashboard)
	if err != nil {
		return nil, err
	}
	req, err := c.newRequest("POST", "/api/dashboards/db", nil, bytes.NewBuffer(data))
	if err != nil {
		return nil, err
	}

	resp, err := c.Do(req)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != 200 {
		return nil, errors.New(resp.Status)
	}

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

	result := &DashboardSaveResponse{}
	err = json.Unmarshal(data, &result)
	return result, err
}

// Deprecated: Starting from Grafana v5.0. Please update to use DashboardByUID instead.
func (c *Client) Dashboard(slug string) (*Dashboard, error) {
	return c.dashboard(fmt.Sprintf("/api/dashboards/db/%s", slug))
}

func (c *Client) DashboardByUID(uid string) (*Dashboard, error) {
	return c.dashboard(fmt.Sprintf("/api/dashboards/uid/%s", uid))
}

func (c *Client) dashboard(path string) (*Dashboard, error) {
	req, err := c.newRequest("GET", path, nil, nil)
	if err != nil {
		return nil, err
	}

	resp, err := c.Do(req)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != 200 {
		return nil, errors.New(resp.Status)
	}

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

	result := &Dashboard{}
	err = json.Unmarshal(data, &result)
	result.Folder = result.Meta.Folder
	if os.Getenv("GF_LOG") != "" {
		log.Printf("got back dashboard response  %s", data)
	}
	return result, err
}

// Deprecated: Starting from Grafana v5.0. Please update to use DeleteDashboardByUID instead.
func (c *Client) DeleteDashboard(slug string) error {
	return c.deleteDashboard(fmt.Sprintf("/api/dashboards/db/%s", slug))
}

func (c *Client) DeleteDashboardByUID(uid string) error {
	return c.deleteDashboard(fmt.Sprintf("/api/dashboards/uid/%s", uid))
}

func (c *Client) deleteDashboard(path string) error {
	req, err := c.newRequest("DELETE", path, nil, nil)
	if err != nil {
		return err
	}

	resp, err := c.Do(req)
	if err != nil {
		return err
	}
	if resp.StatusCode != 200 {
		return errors.New(resp.Status)
	}

	return nil
}<|endoftext|>


<commit_before>package qbit

import (
	"fmt"
	"strings"
)

// function generates a table column
func NewColumn(name string, t *Type, constraints []Constraint) Column {
	return Column{
		Name:        name,
		Type:        t,
		Constraints: constraints,
	}
}

type Column struct {
	Name        string
	Type        *Type
	Constraints []Constraint
}

func (c *Column) Sql() string {

	constraints := []string{}
	for _, v := range c.Constraints {
		constraints = append(constraints, v.Name)
	}

	colPieces := []string{
		c.Name,
		c.Type.Sql(),
	}

	if len(constraints) > 0 {
		colPieces = append(colPieces, strings.Join(constraints, " "))
	}

	return fmt.Sprintf("%s", strings.Join(colPieces, " "))
}
<commit_msg>fix lint errors of column<commit_after>package qbit

import (
	"fmt"
	"strings"
)

// NewColumn creates and returns a table column
func NewColumn(name string, t *Type, constraints []Constraint) Column {
	return Column{
		Name:        name,
		Type:        t,
		Constraints: constraints,
	}
}

// Column is the base abstraction for table struct columns
type Column struct {
	Name        string
	Type        *Type
	Constraints []Constraint
}

// SQL returns column as an sql statement
func (c *Column) SQL() string {

	constraints := []string{}
	for _, v := range c.Constraints {
		constraints = append(constraints, v.Name)
	}

	colPieces := []string{
		c.Name,
		c.Type.Sql(),
	}

	if len(constraints) > 0 {
		colPieces = append(colPieces, strings.Join(constraints, " "))
	}

	return fmt.Sprintf("%s", strings.Join(colPieces, " "))
}
<|endoftext|>


<commit_before>package storage

import (
	"archive/zip"
	"bytes"
	"fmt"
	"github.com/viant/toolbox"
	"io"
	"io/ioutil"
	"path"
	"strings"
)

type CopyHandler func(sourceObject Object, source io.Reader, destinationService Service, destinationURL string) error
type ModificationHandler func(reader io.ReadCloser) (io.ReadCloser, error)

func urlPath(URL string) string {
	var result = URL
	schemaPosition := strings.Index(URL, "://")
	if schemaPosition != -1 {
		result = string(URL[schemaPosition+3:])
	}
	pathRoot := strings.Index(result, "/")
	if pathRoot > 0 {
		result = string(result[pathRoot:])
	}
	if strings.HasSuffix(result, "/") {
		result = string(result[:len(result)-1])
	}

	return result
}

func truncatePath(path string) string {
	if len(path) <= 1 {
		return path
	}
	if strings.HasSuffix(path, "/") {
		return string(path[:len(path)-1])
	}
	return path
}

func copyStorageContent(sourceService Service, sourceURL string, destinationService Service, destinationURL string, modifyContentHandler ModificationHandler, subPath string, copyHandler CopyHandler) error {
	sourceListURL := sourceURL
	if subPath != "" {
		sourceListURL = toolbox.URLPathJoin(sourceURL, subPath)
	}
	objects, err := sourceService.List(sourceListURL)
	if err != nil {
		return err
	}
	var objectRelativePath string
	sourceURLPath := urlPath(sourceURL)

	for _, object := range objects {
		var objectURLPath = urlPath(object.URL())
		if object.IsFolder() {
			if truncatePath(sourceURLPath) == truncatePath(objectURLPath) {
				continue
			}

			if subPath != "" && objectURLPath == toolbox.URLPathJoin(sourceURLPath, subPath) {
				continue
			}
		}

		if len(objectURLPath) > len(sourceURLPath) {
			objectRelativePath = objectURLPath[len(sourceURLPath):]
			if strings.HasPrefix(objectRelativePath, "/") {
				objectRelativePath = string(objectRelativePath[1:])
			}
		}
		var destinationObjectURL = destinationURL
		if objectRelativePath != "" {
			destinationObjectURL = toolbox.URLPathJoin(destinationURL, objectRelativePath)
		}

		if object.IsContent() {
			reader, err := sourceService.Download(object)
			if err != nil {
				err = fmt.Errorf("unable download, %v -> %v, %v", object.URL(), destinationObjectURL, err)
				return err
			}
			defer reader.Close()

			if modifyContentHandler != nil {

				content, err := ioutil.ReadAll(reader)
				if err != nil {
					return err
				}
				reader = ioutil.NopCloser(bytes.NewReader(content))
				reader, err = modifyContentHandler(reader)
				if err != nil {
					err = fmt.Errorf("unable modify content, %v %v %v", object.URL(), destinationObjectURL, err)
					return err
				}

			}
			if subPath == "" {
				_, sourceName := path.Split(object.URL())
				_, destinationName := path.Split(destinationURL)
				if strings.HasSuffix(destinationObjectURL, "/") {
					destinationObjectURL = toolbox.URLPathJoin(destinationObjectURL, sourceName)
				} else {
					destinationObject, _ := destinationService.StorageObject(destinationObjectURL)
					if destinationObject != nil && destinationObject.IsFolder() {
						destinationObjectURL = toolbox.URLPathJoin(destinationObjectURL, sourceName)
					} else if destinationName != sourceName {
						if !strings.Contains(destinationName, ".") {
							destinationObjectURL = toolbox.URLPathJoin(destinationURL, sourceName)
						}

					}
				}
			}

			err = copyHandler(object, reader, destinationService, destinationObjectURL)
			if err != nil {
				return err
			}

		} else {
			err = copyStorageContent(sourceService, sourceURL, destinationService, destinationURL, modifyContentHandler, objectRelativePath, copyHandler)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

func copySourceToDestination(sourceObject Object, reader io.Reader, destinationService Service, destinationURL string) error {
	err := destinationService.Upload(destinationURL, reader)
	if err != nil {
		err = fmt.Errorf("unable upload, %v %v %v", sourceObject.URL(), destinationURL, err)
	}
	return err
}

func getArchiveCopyHandler(archive *zip.Writer, parentURL string) CopyHandler {

	return func(sourceObject Object, reader io.Reader, destinationService Service, destinationURL string) error {
		var _, relativePath = toolbox.URLSplit(destinationURL)
		if destinationURL != parentURL {
			relativePath = strings.Replace(destinationURL, parentURL, "", 1)
		}

		header, err := zip.FileInfoHeader(sourceObject.FileInfo())
		if err != nil {
			return err
		}
		header.Method = zip.Deflate
		header.Name = relativePath
		writer, err := archive.CreateHeader(header)
		if err != nil {
			return err
		}
		_, err = io.Copy(writer, reader)
		return err
	}
}

//Copy downloads objects from source URL to upload them to destination URL.
func Copy(sourceService Service, sourceURL string, destinationService Service, destinationURL string, modifyContentHandler ModificationHandler, copyHandler CopyHandler) (err error) {
	if copyHandler == nil {
		copyHandler = copySourceToDestination
	}
	if strings.HasSuffix(sourceURL, "//") {
		sourceURL = string(sourceURL[:len(sourceURL)-1])
	}
	err = copyStorageContent(sourceService, sourceURL, destinationService, destinationURL, modifyContentHandler, "", copyHandler)
	if err != nil {
		err = fmt.Errorf("failed to copy %v -> %v: %v", sourceURL, destinationURL, err)
	}
	return err
}

//Archive archives supplied URL assets into zip writer
func Archive(service Service, URL string, writer *zip.Writer) error {
	memService := NewMemoryService()
	var destURL = "mem:///dev/nul"
	return Copy(service, URL, memService, destURL, nil, getArchiveCopyHandler(writer, destURL))
}

func getArchiveCopyHandlerWithFilter(archive *zip.Writer, parentURL string, predicate func(candidate Object) bool) CopyHandler {
	return func(sourceObject Object, reader io.Reader, destinationService Service, destinationURL string) error {
		if !predicate(sourceObject) {
			return nil
		}
		var _, relativePath = toolbox.URLSplit(destinationURL)
		if destinationURL != parentURL {
			relativePath = strings.Replace(destinationURL, parentURL, "", 1)
		}
		header, err := zip.FileInfoHeader(sourceObject.FileInfo())
		if err != nil {
			return err
		}
		header.Method = zip.Store

		if strings.HasPrefix(relativePath, "/") {
			relativePath = string(relativePath[1:])
		}
		header.Name = relativePath
		writer, err := archive.CreateHeader(header)
		if err != nil {
			return err
		}
		_, err = io.Copy(writer, reader)
		return err
	}
}

//Archive archives supplied URL assets into zip writer with supplied filter
func ArchiveWithFilter(service Service, URL string, writer *zip.Writer, predicate func(candidate Object) bool) error {
	memService := NewMemoryService()
	var destURL = "mem:///dev/nul"
	return Copy(service, URL, memService, destURL, nil, getArchiveCopyHandlerWithFilter(writer, destURL, predicate))
}
<commit_msg>added tar archive<commit_after>package storage

import (
	"archive/tar"
	"archive/zip"
	"bytes"
	"fmt"
	"github.com/viant/toolbox"
	"io"
	"io/ioutil"
	"path"
	"strings"
)

type CopyHandler func(sourceObject Object, source io.Reader, destinationService Service, destinationURL string) error
type ModificationHandler func(reader io.ReadCloser) (io.ReadCloser, error)

func urlPath(URL string) string {
	var result = URL
	schemaPosition := strings.Index(URL, "://")
	if schemaPosition != -1 {
		result = string(URL[schemaPosition+3:])
	}
	pathRoot := strings.Index(result, "/")
	if pathRoot > 0 {
		result = string(result[pathRoot:])
	}
	if strings.HasSuffix(result, "/") {
		result = string(result[:len(result)-1])
	}

	return result
}

func truncatePath(path string) string {
	if len(path) <= 1 {
		return path
	}
	if strings.HasSuffix(path, "/") {
		return string(path[:len(path)-1])
	}
	return path
}

func copyStorageContent(sourceService Service, sourceURL string, destinationService Service, destinationURL string, modifyContentHandler ModificationHandler, subPath string, copyHandler CopyHandler) error {
	sourceListURL := sourceURL
	if subPath != "" {
		sourceListURL = toolbox.URLPathJoin(sourceURL, subPath)
	}
	objects, err := sourceService.List(sourceListURL)
	if err != nil {
		return err
	}
	var objectRelativePath string
	sourceURLPath := urlPath(sourceURL)

	for _, object := range objects {
		var objectURLPath = urlPath(object.URL())
		if object.IsFolder() {
			if truncatePath(sourceURLPath) == truncatePath(objectURLPath) {
				continue
			}

			if subPath != "" && objectURLPath == toolbox.URLPathJoin(sourceURLPath, subPath) {
				continue
			}
		}

		if len(objectURLPath) > len(sourceURLPath) {
			objectRelativePath = objectURLPath[len(sourceURLPath):]
			if strings.HasPrefix(objectRelativePath, "/") {
				objectRelativePath = string(objectRelativePath[1:])
			}
		}
		var destinationObjectURL = destinationURL
		if objectRelativePath != "" {
			destinationObjectURL = toolbox.URLPathJoin(destinationURL, objectRelativePath)
		}

		if object.IsContent() {
			reader, err := sourceService.Download(object)
			if err != nil {
				err = fmt.Errorf("unable download, %v -> %v, %v", object.URL(), destinationObjectURL, err)
				return err
			}
			defer reader.Close()

			if modifyContentHandler != nil {

				content, err := ioutil.ReadAll(reader)
				if err != nil {
					return err
				}
				reader = ioutil.NopCloser(bytes.NewReader(content))
				reader, err = modifyContentHandler(reader)
				if err != nil {
					err = fmt.Errorf("unable modify content, %v %v %v", object.URL(), destinationObjectURL, err)
					return err
				}

			}
			if subPath == "" {
				_, sourceName := path.Split(object.URL())
				_, destinationName := path.Split(destinationURL)
				if strings.HasSuffix(destinationObjectURL, "/") {
					destinationObjectURL = toolbox.URLPathJoin(destinationObjectURL, sourceName)
				} else {
					destinationObject, _ := destinationService.StorageObject(destinationObjectURL)
					if destinationObject != nil && destinationObject.IsFolder() {
						destinationObjectURL = toolbox.URLPathJoin(destinationObjectURL, sourceName)
					} else if destinationName != sourceName {
						if !strings.Contains(destinationName, ".") {
							destinationObjectURL = toolbox.URLPathJoin(destinationURL, sourceName)
						}

					}
				}
			}

			err = copyHandler(object, reader, destinationService, destinationObjectURL)
			if err != nil {
				return err
			}

		} else {
			err = copyStorageContent(sourceService, sourceURL, destinationService, destinationURL, modifyContentHandler, objectRelativePath, copyHandler)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

func copySourceToDestination(sourceObject Object, reader io.Reader, destinationService Service, destinationURL string) error {
	err := destinationService.Upload(destinationURL, reader)
	if err != nil {
		err = fmt.Errorf("unable upload, %v %v %v", sourceObject.URL(), destinationURL, err)
	}
	return err
}

func getArchiveCopyHandler(archive *zip.Writer, parentURL string) CopyHandler {

	return func(sourceObject Object, reader io.Reader, destinationService Service, destinationURL string) error {
		var _, relativePath = toolbox.URLSplit(destinationURL)
		if destinationURL != parentURL {
			relativePath = strings.Replace(destinationURL, parentURL, "", 1)
		}

		header, err := zip.FileInfoHeader(sourceObject.FileInfo())
		if err != nil {
			return err
		}
		header.Method = zip.Deflate
		header.Name = relativePath
		writer, err := archive.CreateHeader(header)
		if err != nil {
			return err
		}
		_, err = io.Copy(writer, reader)
		return err
	}
}

//Copy downloads objects from source URL to upload them to destination URL.
func Copy(sourceService Service, sourceURL string, destinationService Service, destinationURL string, modifyContentHandler ModificationHandler, copyHandler CopyHandler) (err error) {
	if copyHandler == nil {
		copyHandler = copySourceToDestination
	}
	if strings.HasSuffix(sourceURL, "//") {
		sourceURL = string(sourceURL[:len(sourceURL)-1])
	}
	err = copyStorageContent(sourceService, sourceURL, destinationService, destinationURL, modifyContentHandler, "", copyHandler)
	if err != nil {
		err = fmt.Errorf("failed to copy %v -> %v: %v", sourceURL, destinationURL, err)
	}
	return err
}

//Archive archives supplied URL assets into zip writer
func Archive(service Service, URL string, writer *zip.Writer) error {
	memService := NewMemoryService()
	var destURL = "mem:///dev/nul"
	return Copy(service, URL, memService, destURL, nil, getArchiveCopyHandler(writer, destURL))
}

func getArchiveCopyHandlerWithFilter(archive *zip.Writer, parentURL string, predicate func(candidate Object) bool) CopyHandler {
	return func(sourceObject Object, reader io.Reader, destinationService Service, destinationURL string) error {
		if !predicate(sourceObject) {
			return nil
		}
		var _, relativePath = toolbox.URLSplit(destinationURL)
		if destinationURL != parentURL {
			relativePath = strings.Replace(destinationURL, parentURL, "", 1)
		}
		header, err := zip.FileInfoHeader(sourceObject.FileInfo())
		if err != nil {
			return err
		}
		header.Method = zip.Store

		if strings.HasPrefix(relativePath, "/") {
			relativePath = string(relativePath[1:])
		}
		header.Name = relativePath
		writer, err := archive.CreateHeader(header)
		if err != nil {
			return err
		}
		_, err = io.Copy(writer, reader)
		return err
	}
}

//Archive archives supplied URL assets into zip writer with supplied filter
func ArchiveWithFilter(service Service, URL string, writer *zip.Writer, predicate func(candidate Object) bool) error {
	memService := NewMemoryService()
	var destURL = "mem:///dev/nul"
	return Copy(service, URL, memService, destURL, nil, getArchiveCopyHandlerWithFilter(writer, destURL, predicate))
}

func getTarCopyHandler(archive *tar.Writer, parentURL string) CopyHandler {
	return func(sourceObject Object, reader io.Reader, destinationService Service, destinationURL string) error {
		var _, relativePath = toolbox.URLSplit(destinationURL)
		if destinationURL != parentURL {
			relativePath = strings.Replace(destinationURL, parentURL, "", 1)
		}

		contents := new(bytes.Buffer)
		if _, err := io.Copy(contents, reader); err != nil {
			return err
		}
		data := contents.Bytes()
		if strings.HasPrefix(relativePath, "/") {
			relativePath = string(relativePath[1:])
		}
		tarHeader := &tar.Header{
			Name: relativePath,
			Size: int64(len(data)),
		}
		if err := archive.WriteHeader(tarHeader); err != nil {
			return fmt.Errorf(" unable to write tar header, %v", err)
		}
		if _, err := archive.Write(data); err != nil {
			return fmt.Errorf(" unable to write tar content, %v", err)
		}
		return nil
	}
}

//Tar tar archives supplied URL assets into zip writer
func Tar(service Service, URL string, writer *tar.Writer) error {
	memService := NewMemoryService()
	var destURL = "mem:///dev/nul"
	return Copy(service, URL, memService, destURL, nil, getTarCopyHandler(writer, destURL))
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"os/exec"
	"bytes"
	"io/ioutil"
)

func main() {
	dir := "."
	pattern := "*"
	if len(os.Args) == 2 {
		if os.Args[1] == "-h" || os.Args[1] == "--help" {
			usage()
			return
		}
		pattern = os.Args[1]
	}

	files, err := LookupFiles(dir, pattern)
	if err != nil {
		fmt.Printf("%v\n", err.Error())
		return
	}

	allok := true
	for _, file := range files {
		err := ConvGBK2UTF8(file)
		if err != nil {
			fmt.Printf("convert [%v] from GBK to UTF8 failed. %v\n", err.Error())
			allok = false
		}
	}

	if allok {
		fmt.Printf("All convert OK\n")
	}
}

func ConvGBK2UTF8(file string) error {
	//TODO use go buildin function to iconv
	cmd := exec.Command("iconv", "-f", "gbk", "-t", "utf-8", file)
    cmd.Env = os.Environ()
    stdoutput := new(bytes.Buffer)
    erroutput := new(bytes.Buffer)
    cmd.Stdout = stdoutput
    cmd.Stderr = erroutput
    err := cmd.Run()
    if err != nil {
    	return errors.New(err.Error() + " " + erroutput.String())
    }

    bakcmd := exec.Command("mv", file, file + ".bak")
    err = bakcmd.Run()
    if err != nil {
    	return errors.New(err.Error() + " " + erroutput.String())
    }

    tty, err := os.OpenFile(file, os.O_CREATE|os.O_TRUNC, 0755)
	defer func() {
		tty.Close()
	}()

	ioutil.WriteFile(file, stdoutput.Bytes(), 0755)
	return nil
}

func LookupFiles(dir, pattern string) ([]string, error) {
	var files []string = make([]string, 0, 5)

	err := filepath.Walk(dir, func(path string, f os.FileInfo, err error) error {
		if f == nil {
			return err
		}

		if f.IsDir() {
			return nil
		}

		if ok, err := filepath.Match(pattern, f.Name()); err != nil {
			return err
		} else if ok {
			files = append(files, path)
		}
		return nil
	})

	if len(files) == 0 {
		return files, errors.New("Not found any files")
	}

	return files, err
}

func usage() {
	fmt.Printf("usage : %v <pattern>\n", os.Args[0])
	fmt.Printf("Example 1: %v *.cc\n", os.Args[0])
	fmt.Printf("Example 2: %v *\n", os.Args[0])
	fmt.Printf("Example 3: %v dir/*\n", os.Args[0])
}<commit_msg>Add more comments<commit_after>package main

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"os/exec"
	"bytes"
	"io/ioutil"
)

func main() {
	dir := "."
	pattern := "*"
	if len(os.Args) == 2 {
		if os.Args[1] == "-h" || os.Args[1] == "--help" {
			usage()
			return
		}
		pattern = os.Args[1]
	}

	files, err := LookupFiles(dir, pattern)
	if err != nil {
		fmt.Printf("%v\n", err.Error())
		return
	}

	allok := true
	for _, file := range files {
		err := ConvGBK2UTF8(file)
		if err != nil {
			fmt.Printf("convert [%v] from GBK to UTF8 failed. %v\n", file, err.Error())
			allok = false
		} else {
			fmt.Printf("convert [%v] from GBK to UTF8 OK.\n", file)
		}
	}

	if allok {
		fmt.Printf("All convert OK\n")
	}
}

func ConvGBK2UTF8(file string) error {
	//TODO use go buildin function to iconv
	cmd := exec.Command("iconv", "-f", "gbk", "-t", "utf-8", file)
    cmd.Env = os.Environ()
    stdoutput := new(bytes.Buffer)
    erroutput := new(bytes.Buffer)
    cmd.Stdout = stdoutput
    cmd.Stderr = erroutput
    err := cmd.Run()
    if err != nil {
    	return errors.New(err.Error() + " " + erroutput.String())
    }

    bakcmd := exec.Command("mv", file, file + ".bak")
    err = bakcmd.Run()
    if err != nil {
    	return errors.New(err.Error() + " " + erroutput.String())
    }

    tty, err := os.OpenFile(file, os.O_CREATE|os.O_TRUNC, 0755)
	defer func() {
		tty.Close()
	}()

	ioutil.WriteFile(file, stdoutput.Bytes(), 0755)
	return nil
}

func LookupFiles(dir, pattern string) ([]string, error) {
	var files []string = make([]string, 0, 5)

	err := filepath.Walk(dir, func(path string, f os.FileInfo, err error) error {
		if f == nil {
			return err
		}

		if f.IsDir() {
			return nil
		}

		if ok, err := filepath.Match(pattern, f.Name()); err != nil {
			return err
		} else if ok {
			files = append(files, path)
		}
		return nil
	})

	if len(files) == 0 {
		return files, errors.New("Not found any files")
	}

	return files, err
}

func usage() {
	fmt.Printf("usage : %v <pattern>\n", os.Args[0])
	s := `
gbk2utf8 is a tool that will convert a file from GBK encoding to UTF-8 encoding
in position and backup the origin file with a extention file ".bak". Here below
are some simple usage:

gbk2utf8
	This will convert all files in curent dir and subdirs.

gbk2utf8 *.cc
	This will convert all *.cc files in curent dir.

gbk2utf8 somedir/*
	This will convert all *files in somedir and its subdirs
`
	fmt.Printf(s)
}<|endoftext|>


<commit_before>// +build openssl

package crypto

import (
	pb "github.com/libp2p/go-libp2p-core/crypto/pb"

	openssl "github.com/spacemonkeygo/openssl"
)

// define these as separate types so we can add more key types later and reuse
// code.

type opensslPublicKey struct {
	key openssl.PublicKey
}

type opensslPrivateKey struct {
	key openssl.PrivateKey
}

func unmarshalOpensslPrivateKey(b []byte) (opensslPrivateKey, error) {
	sk, err := openssl.LoadPrivateKeyFromDER(b)
	if err != nil {
		return opensslPrivateKey{}, err
	}
	return opensslPrivateKey{sk}, nil
}

func unmarshalOpensslPublicKey(b []byte) (opensslPublicKey, error) {
	sk, err := openssl.LoadPublicKeyFromDER(b)
	if err != nil {
		return opensslPublicKey{}, err
	}
	return opensslPublicKey{sk}, nil
}

// Verify compares a signature against input data
func (pk *opensslPublicKey) Verify(data, sig []byte) (bool, error) {
	err := pk.key.VerifyPKCS1v15(openssl.SHA256_Method, data, sig)
	return err == nil, err
}

func (pk *opensslPublicKey) Type() pb.KeyType {
	switch pk.key.KeyType() {
	case openssl.KeyTypeRSA:
		return pb.KeyType_RSA
	default:
		return -1
	}
}

// Bytes returns protobuf bytes of a public key
func (pk *opensslPublicKey) Bytes() ([]byte, error) {
	return MarshalPublicKey(pk)
}

func (pk *opensslPublicKey) Raw() ([]byte, error) {
	return pk.key.MarshalPKIXPublicKeyDER()
}

// Equals checks whether this key is equal to another
func (pk *opensslPublicKey) Equals(k Key) bool {
	return KeyEqual(pk, k)
}

// Sign returns a signature of the input data
func (sk *opensslPrivateKey) Sign(message []byte) ([]byte, error) {
	return sk.key.SignPKCS1v15(openssl.SHA256_Method, message)
}

// GetPublic returns a public key
func (sk *opensslPrivateKey) GetPublic() PubKey {
	return &opensslPublicKey{sk.key}
}

func (sk *opensslPrivateKey) Type() pb.KeyType {
	switch sk.key.KeyType() {
	case openssl.KeyTypeRSA:
		return pb.KeyType_RSA
	default:
		return -1
	}
}

// Bytes returns protobuf bytes from a private key
func (sk *opensslPrivateKey) Bytes() ([]byte, error) {
	return MarshalPrivateKey(sk)
}

func (sk *opensslPrivateKey) Raw() ([]byte, error) {
	return sk.key.MarshalPKCS1PrivateKeyDER()
}

// Equals checks whether this key is equal to another
func (sk *opensslPrivateKey) Equals(k Key) bool {
	return KeyEqual(sk, k)
}
<commit_msg>fixup: openssl<commit_after>// +build openssl

package crypto

import (
	"bytes"

	pb "github.com/libp2p/go-libp2p-core/crypto/pb"

	openssl "github.com/spacemonkeygo/openssl"
)

// define these as separate types so we can add more key types later and reuse
// code.

type opensslPublicKey struct {
	key openssl.PublicKey
}

type opensslPrivateKey struct {
	key openssl.PrivateKey
}

func unmarshalOpensslPrivateKey(b []byte) (opensslPrivateKey, error) {
	sk, err := openssl.LoadPrivateKeyFromDER(b)
	if err != nil {
		return opensslPrivateKey{}, err
	}
	return opensslPrivateKey{sk}, nil
}

func unmarshalOpensslPublicKey(b []byte) (opensslPublicKey, error) {
	sk, err := openssl.LoadPublicKeyFromDER(b)
	if err != nil {
		return opensslPublicKey{}, err
	}
	return opensslPublicKey{sk}, nil
}

// Verify compares a signature against input data
func (pk *opensslPublicKey) Verify(data, sig []byte) (bool, error) {
	err := pk.key.VerifyPKCS1v15(openssl.SHA256_Method, data, sig)
	return err == nil, err
}

func (pk *opensslPublicKey) Type() pb.KeyType {
	switch pk.key.KeyType() {
	case openssl.KeyTypeRSA:
		return pb.KeyType_RSA
	default:
		return -1
	}
}

// Bytes returns protobuf bytes of a public key
func (pk *opensslPublicKey) Bytes() ([]byte, error) {
	return MarshalPublicKey(pk)
}

func (pk *opensslPublicKey) Raw() ([]byte, error) {
	return pk.key.MarshalPKIXPublicKeyDER()
}

// Equals checks whether this key is equal to another
func (pk *opensslPublicKey) Equals(k Key) bool {
	a, err := pk.Raw()
	if err != nil {
		return false
	}
	b, err := k.Raw()
	if err != nil {
		return false
	}
	return bytes.Equal(a, b)
}

// Sign returns a signature of the input data
func (sk *opensslPrivateKey) Sign(message []byte) ([]byte, error) {
	return sk.key.SignPKCS1v15(openssl.SHA256_Method, message)
}

// GetPublic returns a public key
func (sk *opensslPrivateKey) GetPublic() PubKey {
	return &opensslPublicKey{sk.key}
}

func (sk *opensslPrivateKey) Type() pb.KeyType {
	switch sk.key.KeyType() {
	case openssl.KeyTypeRSA:
		return pb.KeyType_RSA
	default:
		return -1
	}
}

// Bytes returns protobuf bytes from a private key
func (sk *opensslPrivateKey) Bytes() ([]byte, error) {
	return MarshalPrivateKey(sk)
}

func (sk *opensslPrivateKey) Raw() ([]byte, error) {
	return sk.key.MarshalPKCS1PrivateKeyDER()
}

// Equals checks whether this key is equal to another
func (sk *opensslPrivateKey) Equals(k Key) bool {
	a, err := sk.Raw()
	if err != nil {
		return false
	}
	b, err := k.Raw()
	if err != nil {
		return false
	}
	return bytes.Equal(a, b)
}
<|endoftext|>


<commit_before>package store

import (
	"fmt"
	"path"
	"time"

	"github.com/disorganizer/brig/id"
	"github.com/disorganizer/brig/store/wire"
	"github.com/jbenet/go-multihash"
)

var (
	ErrEmptyStage         = fmt.Errorf("Nothing staged. No commit done")
	ErrEmptyCommitMessage = fmt.Errorf("Not doing a commit due to missing messsage")
)

// Merge describes the merge of two stores at one point in history.
type Merge struct {
	// With is the store owner of the store we merged with.
	With id.ID

	// Hash of the commit in the other store we merged with.
	Hash *Hash
}

func (mg *Merge) ToProto() (*wire.Merge, error) {
	return &wire.Merge{
		With: string(mg.With),
		Hash: mg.Hash.Bytes(),
	}, nil
}

func (mg *Merge) FromProto(protoMerge *wire.Merge) error {
	ID, err := id.Cast(protoMerge.With)
	if err != nil {
		return err
	}

	hash, err := multihash.Cast(protoMerge.Hash)
	if err != nil {
		return err
	}

	mg.With = ID
	mg.Hash = &Hash{hash}
	return nil
}

func (mg *Merge) String() string {
	return fmt.Sprintf("merge(%s:%s)", mg.With, mg.Hash.B58String())
}

////////////////////////

type Author struct {
	ident id.ID
	hash  *Hash
}

func (a *Author) ID() id.ID {
	return a.ident
}

func (a *Author) Hash() string {
	return a.hash.B58String()
}

func StageAuthor() *Author {
	return &Author{"unknown", EmptyHash.Clone()}
}

func (a *Author) FromProto(pa *wire.Author) error {
	ident, err := id.Cast(pa.Name)
	if err != nil {
		return err
	}

	mh, err := multihash.FromB58String(pa.Hash)
	if err != nil {
		return err
	}

	a.ident = ident
	a.hash = &Hash{mh}
	return nil
}

func (a *Author) ToProto() (*wire.Author, error) {
	// Author might be nil for the staging commit:
	if a == nil {
		return StageAuthor().ToProto()
	}

	return &wire.Author{
		Name: string(a.ident),
		Hash: a.hash.B58String(),
	}, nil
}

////////////////////////

// Commit groups a change set
type Commit struct {
	// Commit message (might be auto-generated)
	message string

	// Author is the id of the committer.
	author *Author

	// Time at this commit was conceived.
	modTime time.Time

	// Checkpoints is the bag of actual changes.
	changeset []*CheckpointLink

	// Hash of this commit
	hash *Hash

	// TreeHash is the hash of the root node at this point in time
	root *Hash

	// Parent hash (only nil for initial commit)
	parent *Hash

	// store is needed to marshal/unmarshal properly
	fs *FS

	// Merge is set if this is a merge commit (nil otherwise)
	merge *Merge

	id uint64
}

func newEmptyCommit(fs *FS) (*Commit, error) {
	id, err := fs.NextID()
	if err != nil {
		return nil, err
	}

	return &Commit{
		id:      id,
		fs:      fs,
		modTime: time.Now(),
	}, nil
}

func (cm *Commit) String() string {
	return fmt.Sprintf(
		"commit %s/%s <%s>",
		cm.hash.B58String(),
		cm.root.B58String(),
		cm.message,
	)
}

func (cm *Commit) GetChangeset() []*CheckpointLink {
	return cm.changeset
}

func (cm *Commit) FromProto(pnd *wire.Node) error {
	pcm := pnd.Commit
	if pcm == nil {
		return fmt.Errorf("No commit attr in protobuf. Probably not a commit.")
	}

	author := &Author{}
	if err := author.FromProto(pcm.Author); err != nil {
		return err
	}

	modTime := time.Time{}
	if err := modTime.UnmarshalBinary(pnd.ModTime); err != nil {
		return err
	}

	hash, err := multihash.Cast(pnd.Hash)
	if err != nil {
		return err
	}

	root, err := multihash.Cast(pcm.Root)
	if err != nil {
		return err
	}

	var parent multihash.Multihash
	if len(pcm.Parent) > 0 {
		parent, err = multihash.Cast(pcm.Parent)
		if err != nil {
			return err
		}
	}

	var changeset []*CheckpointLink

	for _, pcl := range pcm.Changeset {
		cl := &CheckpointLink{}
		if err := cl.FromProto(pcl); err != nil {
			return err
		}

		changeset = append(changeset, cl)
	}

	protoMergeInfo := pcm.Merge
	if protoMergeInfo != nil {
		mergeInfo := &Merge{}
		if err := mergeInfo.FromProto(protoMergeInfo); err != nil {
			return err
		}

		cm.merge = mergeInfo
	}

	// Set commit data if everything worked:
	cm.id = pnd.ID
	cm.message = pcm.Message
	cm.author = author
	cm.modTime = modTime
	cm.hash = &Hash{hash}
	cm.root = &Hash{root}
	cm.changeset = changeset

	if parent != nil {
		cm.parent = &Hash{parent}
	}
	return nil
}

func (cm *Commit) ExpandProto(pnd *wire.Node) error {
	for _, link := range cm.changeset {
		ckp, err := link.Resolve(cm.fs)
		if err != nil {
			return err
		}

		pckp, err := ckp.ToProto()
		if err != nil {
			return err
		}

		if err := ckp.ExpandProto(cm.fs, pckp); err != nil {
			return err
		}

		pnd.Commit.Checkpoints = append(pnd.Commit.Checkpoints, pckp)
	}

	return nil
}

func (cm *Commit) ToProto() (*wire.Node, error) {
	pcm := &wire.Commit{}
	modTime, err := cm.modTime.MarshalBinary()
	if err != nil {
		return nil, err
	}

	var changeset []*wire.CheckpointLink

	for _, link := range cm.changeset {
		plink, err := link.ToProto()
		if err != nil {
			return nil, err
		}

		changeset = append(changeset, plink)
	}

	if cm.merge != nil {
		pmerge, err := cm.merge.ToProto()
		if err != nil {
			return nil, err
		}

		pcm.Merge = pmerge
	}

	pauthor, err := cm.author.ToProto()
	if err != nil {
		return nil, err
	}

	pcm.Message = cm.message
	pcm.Author = pauthor
	pcm.Root = cm.root.Bytes()
	pcm.Changeset = changeset

	// Check if it's the initial commit:
	var parentHash []byte
	if cm.parent != nil {
		parentHash = cm.parent.Bytes()
	}

	pcm.Parent = parentHash

	hashBytes := cm.hash.Bytes()
	if len(hashBytes) == 0 {
		hashBytes = EmptyHash.Bytes()
	}

	// TODO: Store something more meaningful in 'name':
	return &wire.Node{
		Type:     wire.NodeType_COMMIT,
		NodeSize: 0,
		ModTime:  modTime,
		Hash:     hashBytes,
		Name:     "commit",
		ID:       cm.id,
		Commit:   pcm,
	}, nil
}

/////////////////// METADATA INTERFACE ///////////////////

func (cm *Commit) Name() string {
	return cm.hash.B58String()
}

func (cm *Commit) Path() string {
	return prefixSlash(path.Join(".snapshots", cm.Name()))
}

func (cm *Commit) Size() uint64 {
	root, err := cm.fs.DirectoryByHash(cm.root)
	if err != nil {
		return 0
	}

	return root.Size()
}

func (cm *Commit) Hash() *Hash {
	return cm.hash
}

func (cm *Commit) ModTime() time.Time {
	return cm.modTime
}

func (cm *Commit) ID() uint64 {
	return cm.id
}

/////////////// HIERARCHY INTERFACE ///////////////

func (cm *Commit) NChildren() int {
	return 1
}

func (cm *Commit) Child(name string) (Node, error) {
	return cm.fs.DirectoryByHash(cm.root)
}

func (cm *Commit) Parent() (Node, error) {
	return cm.fs.CommitByHash(cm.parent)
}

func (cm *Commit) SetParent(nd Node) error {
	cm.parent = nd.Hash()
	return nil
}

func (cm *Commit) GetType() NodeType {
	return NodeTypeCommit
}

///////////////////////////////
/// OWN COMMIT FUNCTIONALITY //
///////////////////////////////

func (cm *Commit) Root() *Hash {
	return cm.root
}

func (cm *Commit) AddCheckpointLink(cl *CheckpointLink) {
	cm.changeset = append(cm.changeset, cl)
}

func (cm *Commit) SetRoot(root *Hash) error {
	cm.root = root.Clone()
	return nil
}

func (cm *Commit) Finalize(author *Author, message string, parent *Commit) error {
	cm.message = message

	if parent != nil {
		if err := cm.SetParent(parent); err != nil {
			return err
		}
	}

	// This is inefficient, but is supposed to be easy to understand
	// while this is still playground stuff.
	s := ""
	s += fmt.Sprintf("Message: %s\n", cm.message)
	s += fmt.Sprintf("Author:  %s\n", cm.author)
	if parent != nil {
		s += fmt.Sprintf("Parent:  %s\n", parent.Hash().B58String())
	}

	hash := cm.root.Clone()
	if err := hash.MixIn([]byte(s)); err != nil {
		return err
	}

	cm.hash = hash
	cm.modTime = time.Now()
	return nil
}
<commit_msg>store: commit.Parent() should not crash for first commit<commit_after>package store

import (
	"fmt"
	"path"
	"time"

	"github.com/disorganizer/brig/id"
	"github.com/disorganizer/brig/store/wire"
	"github.com/jbenet/go-multihash"
)

var (
	ErrEmptyStage         = fmt.Errorf("Nothing staged. No commit done")
	ErrEmptyCommitMessage = fmt.Errorf("Not doing a commit due to missing messsage")
)

// Merge describes the merge of two stores at one point in history.
type Merge struct {
	// With is the store owner of the store we merged with.
	With id.ID

	// Hash of the commit in the other store we merged with.
	Hash *Hash
}

func (mg *Merge) ToProto() (*wire.Merge, error) {
	return &wire.Merge{
		With: string(mg.With),
		Hash: mg.Hash.Bytes(),
	}, nil
}

func (mg *Merge) FromProto(protoMerge *wire.Merge) error {
	ID, err := id.Cast(protoMerge.With)
	if err != nil {
		return err
	}

	hash, err := multihash.Cast(protoMerge.Hash)
	if err != nil {
		return err
	}

	mg.With = ID
	mg.Hash = &Hash{hash}
	return nil
}

func (mg *Merge) String() string {
	return fmt.Sprintf("merge(%s:%s)", mg.With, mg.Hash.B58String())
}

////////////////////////

type Author struct {
	ident id.ID
	hash  *Hash
}

func (a *Author) ID() id.ID {
	return a.ident
}

func (a *Author) Hash() string {
	return a.hash.B58String()
}

func StageAuthor() *Author {
	return &Author{"unknown", EmptyHash.Clone()}
}

func (a *Author) FromProto(pa *wire.Author) error {
	ident, err := id.Cast(pa.Name)
	if err != nil {
		return err
	}

	mh, err := multihash.FromB58String(pa.Hash)
	if err != nil {
		return err
	}

	a.ident = ident
	a.hash = &Hash{mh}
	return nil
}

func (a *Author) ToProto() (*wire.Author, error) {
	// Author might be nil for the staging commit:
	if a == nil {
		return StageAuthor().ToProto()
	}

	return &wire.Author{
		Name: string(a.ident),
		Hash: a.hash.B58String(),
	}, nil
}

////////////////////////

// Commit groups a change set
type Commit struct {
	// Commit message (might be auto-generated)
	message string

	// Author is the id of the committer.
	author *Author

	// Time at this commit was conceived.
	modTime time.Time

	// Checkpoints is the bag of actual changes.
	changeset []*CheckpointLink

	// Hash of this commit
	hash *Hash

	// TreeHash is the hash of the root node at this point in time
	root *Hash

	// Parent hash (only nil for initial commit)
	parent *Hash

	// store is needed to marshal/unmarshal properly
	fs *FS

	// Merge is set if this is a merge commit (nil otherwise)
	merge *Merge

	id uint64
}

func newEmptyCommit(fs *FS) (*Commit, error) {
	id, err := fs.NextID()
	if err != nil {
		return nil, err
	}

	return &Commit{
		id:      id,
		fs:      fs,
		modTime: time.Now(),
	}, nil
}

func (cm *Commit) String() string {
	return fmt.Sprintf(
		"commit %s/%s <%s>",
		cm.hash.B58String(),
		cm.root.B58String(),
		cm.message,
	)
}

func (cm *Commit) GetChangeset() []*CheckpointLink {
	return cm.changeset
}

func (cm *Commit) FromProto(pnd *wire.Node) error {
	pcm := pnd.Commit
	if pcm == nil {
		return fmt.Errorf("No commit attr in protobuf. Probably not a commit.")
	}

	author := &Author{}
	if err := author.FromProto(pcm.Author); err != nil {
		return err
	}

	modTime := time.Time{}
	if err := modTime.UnmarshalBinary(pnd.ModTime); err != nil {
		return err
	}

	hash, err := multihash.Cast(pnd.Hash)
	if err != nil {
		return err
	}

	root, err := multihash.Cast(pcm.Root)
	if err != nil {
		return err
	}

	var parent multihash.Multihash
	if len(pcm.Parent) > 0 {
		parent, err = multihash.Cast(pcm.Parent)
		if err != nil {
			return err
		}
	}

	var changeset []*CheckpointLink

	for _, pcl := range pcm.Changeset {
		cl := &CheckpointLink{}
		if err := cl.FromProto(pcl); err != nil {
			return err
		}

		changeset = append(changeset, cl)
	}

	protoMergeInfo := pcm.Merge
	if protoMergeInfo != nil {
		mergeInfo := &Merge{}
		if err := mergeInfo.FromProto(protoMergeInfo); err != nil {
			return err
		}

		cm.merge = mergeInfo
	}

	// Set commit data if everything worked:
	cm.id = pnd.ID
	cm.message = pcm.Message
	cm.author = author
	cm.modTime = modTime
	cm.hash = &Hash{hash}
	cm.root = &Hash{root}
	cm.changeset = changeset

	if parent != nil {
		cm.parent = &Hash{parent}
	}
	return nil
}

func (cm *Commit) ExpandProto(pnd *wire.Node) error {
	for _, link := range cm.changeset {
		ckp, err := link.Resolve(cm.fs)
		if err != nil {
			return err
		}

		pckp, err := ckp.ToProto()
		if err != nil {
			return err
		}

		if err := ckp.ExpandProto(cm.fs, pckp); err != nil {
			return err
		}

		pnd.Commit.Checkpoints = append(pnd.Commit.Checkpoints, pckp)
	}

	return nil
}

func (cm *Commit) ToProto() (*wire.Node, error) {
	pcm := &wire.Commit{}
	modTime, err := cm.modTime.MarshalBinary()
	if err != nil {
		return nil, err
	}

	var changeset []*wire.CheckpointLink

	for _, link := range cm.changeset {
		plink, err := link.ToProto()
		if err != nil {
			return nil, err
		}

		changeset = append(changeset, plink)
	}

	if cm.merge != nil {
		pmerge, err := cm.merge.ToProto()
		if err != nil {
			return nil, err
		}

		pcm.Merge = pmerge
	}

	pauthor, err := cm.author.ToProto()
	if err != nil {
		return nil, err
	}

	pcm.Message = cm.message
	pcm.Author = pauthor
	pcm.Root = cm.root.Bytes()
	pcm.Changeset = changeset

	// Check if it's the initial commit:
	var parentHash []byte
	if cm.parent != nil {
		parentHash = cm.parent.Bytes()
	}

	pcm.Parent = parentHash

	hashBytes := cm.hash.Bytes()
	if len(hashBytes) == 0 {
		hashBytes = EmptyHash.Bytes()
	}

	// TODO: Store something more meaningful in 'name':
	return &wire.Node{
		Type:     wire.NodeType_COMMIT,
		NodeSize: 0,
		ModTime:  modTime,
		Hash:     hashBytes,
		Name:     "commit",
		ID:       cm.id,
		Commit:   pcm,
	}, nil
}

/////////////////// METADATA INTERFACE ///////////////////

func (cm *Commit) Name() string {
	return cm.hash.B58String()
}

func (cm *Commit) Path() string {
	return prefixSlash(path.Join(".snapshots", cm.Name()))
}

func (cm *Commit) Size() uint64 {
	root, err := cm.fs.DirectoryByHash(cm.root)
	if err != nil {
		return 0
	}

	return root.Size()
}

func (cm *Commit) Hash() *Hash {
	return cm.hash
}

func (cm *Commit) ModTime() time.Time {
	return cm.modTime
}

func (cm *Commit) ID() uint64 {
	return cm.id
}

/////////////// HIERARCHY INTERFACE ///////////////

func (cm *Commit) NChildren() int {
	return 1
}

func (cm *Commit) Child(name string) (Node, error) {
	return cm.fs.DirectoryByHash(cm.root)
}

func (cm *Commit) Parent() (Node, error) {
	if cm.parent == nil {
		return nil, nil
	}

	return cm.fs.CommitByHash(cm.parent)
}

func (cm *Commit) SetParent(nd Node) error {
	cm.parent = nd.Hash()
	return nil
}

func (cm *Commit) GetType() NodeType {
	return NodeTypeCommit
}

///////////////////////////////
/// OWN COMMIT FUNCTIONALITY //
///////////////////////////////

func (cm *Commit) Root() *Hash {
	return cm.root
}

func (cm *Commit) AddCheckpointLink(cl *CheckpointLink) {
	cm.changeset = append(cm.changeset, cl)
}

func (cm *Commit) SetRoot(root *Hash) error {
	cm.root = root.Clone()
	return nil
}

func (cm *Commit) Finalize(author *Author, message string, parent *Commit) error {
	cm.message = message

	if parent != nil {
		if err := cm.SetParent(parent); err != nil {
			return err
		}
	}

	// This is inefficient, but is supposed to be easy to understand
	// while this is still playground stuff.
	s := ""
	s += fmt.Sprintf("Message: %s\n", cm.message)
	s += fmt.Sprintf("Author:  %s\n", cm.author)
	if parent != nil {
		s += fmt.Sprintf("Parent:  %s\n", parent.Hash().B58String())
	}

	hash := cm.root.Clone()
	if err := hash.MixIn([]byte(s)); err != nil {
		return err
	}

	cm.hash = hash
	cm.modTime = time.Now()
	return nil
}
<|endoftext|>