<commit_before>package main

type SplitType bool

const (
	VerticalSplit   = false
	HorizontalSplit = true
)

type Node interface {
	VSplit(buf *Buffer)
	HSplit(buf *Buffer)
	String() string
}

type LeafNode struct {
	view *View

	parent *SplitTree
}

func NewLeafNode(v *View, parent *SplitTree) *LeafNode {
	n := new(LeafNode)
	n.view = v
	n.view.splitNode = n
	n.parent = parent
	return n
}

type SplitTree struct {
	kind SplitType

	parent   *SplitTree
	children []Node

	x int
	y int

	width  int
	height int

	tabNum int
}

func (l *LeafNode) VSplit(buf *Buffer) {
	tab := tabs[l.parent.tabNum]
	if l.parent.kind == VerticalSplit {
		newView := NewView(buf)
		newView.TabNum = l.parent.tabNum
		newView.Num = len(tab.views)
		l.parent.children = append(l.parent.children, NewLeafNode(newView, l.parent))

		tab.curView++
		tab.views = append(tab.views, newView)
	} else {
		s := new(SplitTree)
		s.kind = VerticalSplit
		s.parent = l.parent
		newView := NewView(buf)
		newView.TabNum = l.parent.tabNum
		newView.Num = len(tab.views)
		s.children = []Node{l, NewLeafNode(newView, s)}
		l.parent.children[search(l.parent.children, l)] = s
		l.parent = s

		tab.curView++
		tab.views = append(tab.views, newView)
	}
}

func (l *LeafNode) HSplit(buf *Buffer) {
	tab := tabs[l.parent.tabNum]
	if l.parent.kind == HorizontalSplit {
		newView := NewView(buf)
		newView.TabNum = l.parent.tabNum
		newView.Num = len(tab.views)
		l.parent.children = append(l.parent.children, NewLeafNode(newView, l.parent))

		tab.curView++
		tab.views = append(tab.views, newView)
	} else {
		s := new(SplitTree)
		s.kind = HorizontalSplit
		s.parent = l.parent
		newView := NewView(buf)
		newView.TabNum = l.parent.tabNum
		newView.Num = len(tab.views)
		s.children = []Node{l, NewLeafNode(newView, s)}
		l.parent.children[search(l.parent.children, l)] = s
		l.parent = s

		tab.curView++
		tab.views = append(tab.views, newView)
	}
}

func (l *LeafNode) Delete() {
	i := search(l.parent.children, l)

	copy(l.parent.children[i:], l.parent.children[i+1:])
	l.parent.children[len(l.parent.children)-1] = nil
	l.parent.children = l.parent.children[:len(l.parent.children)-1]

	tab := tabs[l.parent.tabNum]
	j := findView(tab.views, l.view)
	copy(tab.views[j:], tab.views[j+1:])
	tab.views[len(tab.views)-1] = nil // or the zero value of T
	tab.views = tab.views[:len(tab.views)-1]

	for i, v := range tab.views {
		v.Num = i
	}
	if tab.curView > 0 {
		tab.curView--
	}
}

func (s *SplitTree) Cleanup() {
	for i, node := range s.children {
		if n, ok := node.(*SplitTree); ok {
			if len(n.children) == 1 {
				if _, ok := n.children[0].(*LeafNode); ok {
					s.children[i] = n.children[0]
					continue
				}
			}
			n.Cleanup()
		}
	}
}

func (s *SplitTree) ResizeSplits() {
	for i, node := range s.children {
		if n, ok := node.(*LeafNode); ok {
			if s.kind == VerticalSplit {
				n.view.width = s.width / len(s.children)
				n.view.height = s.height

				n.view.x = s.x + n.view.width*i
				n.view.y = s.y
			} else {
				n.view.height = s.height / len(s.children)
				n.view.width = s.width

				n.view.y = s.y + n.view.height*i
				n.view.x = s.x
			}
			// n.view.ToggleStatusLine()
			_, screenH := screen.Size()
			if settings["statusline"].(bool) || (n.view.y+n.view.height) != screenH-1 {
				n.view.height--
			}

			n.view.ToggleTabbar()
			n.view.matches = Match(n.view)
		} else if n, ok := node.(*SplitTree); ok {
			if s.kind == VerticalSplit {
				n.width = s.width / len(s.children)
				n.height = s.height

				n.x = s.x + n.width*i
				n.y = s.y
			} else {
				n.height = s.height / len(s.children)
				n.width = s.width

				n.y = s.y + n.height*i
				n.x = s.x
			}
			n.ResizeSplits()
		}
	}
}

func (l *LeafNode) String() string {
	return l.view.Buf.Name
}

func search(haystack []Node, needle Node) int {
	for i, x := range haystack {
		if x == needle {
			return i
		}
	}
	return 0
}

func findView(haystack []*View, needle *View) int {
	for i, x := range haystack {
		if x == needle {
			return i
		}
	}
	return 0
}

func (s *SplitTree) VSplit(buf *Buffer) {}
func (s *SplitTree) HSplit(buf *Buffer) {}

func (s *SplitTree) String() string {
	str := "["
	for _, child := range s.children {
		str += child.String() + ", "
	}
	return str + "]"
}
<commit_msg>Set parents correctly on split nodes<commit_after>package main

type SplitType bool

const (
	VerticalSplit   = false
	HorizontalSplit = true
)

type Node interface {
	VSplit(buf *Buffer)
	HSplit(buf *Buffer)
	String() string
}

type LeafNode struct {
	view *View

	parent *SplitTree
}

func NewLeafNode(v *View, parent *SplitTree) *LeafNode {
	n := new(LeafNode)
	n.view = v
	n.view.splitNode = n
	n.parent = parent
	return n
}

type SplitTree struct {
	kind SplitType

	parent   *SplitTree
	children []Node

	x int
	y int

	width  int
	height int

	tabNum int
}

func (l *LeafNode) VSplit(buf *Buffer) {
	tab := tabs[l.parent.tabNum]
	if l.parent.kind == VerticalSplit {
		newView := NewView(buf)
		newView.TabNum = l.parent.tabNum
		newView.Num = len(tab.views)
		l.parent.children = append(l.parent.children, NewLeafNode(newView, l.parent))

		tab.curView++
		tab.views = append(tab.views, newView)
	} else {
		s := new(SplitTree)
		s.kind = VerticalSplit
		s.parent = l.parent
		newView := NewView(buf)
		newView.TabNum = l.parent.tabNum
		newView.Num = len(tab.views)
		s.children = []Node{l, NewLeafNode(newView, s)}
		l.parent.children[search(l.parent.children, l)] = s
		l.parent = s

		tab.curView++
		tab.views = append(tab.views, newView)
	}
}

func (l *LeafNode) HSplit(buf *Buffer) {
	tab := tabs[l.parent.tabNum]
	if l.parent.kind == HorizontalSplit {
		newView := NewView(buf)
		newView.TabNum = l.parent.tabNum
		newView.Num = len(tab.views)
		l.parent.children = append(l.parent.children, NewLeafNode(newView, l.parent))

		tab.curView++
		tab.views = append(tab.views, newView)
	} else {
		s := new(SplitTree)
		s.kind = HorizontalSplit
		s.parent = l.parent
		newView := NewView(buf)
		newView.TabNum = l.parent.tabNum
		newView.Num = len(tab.views)
		s.children = []Node{l, NewLeafNode(newView, s)}
		l.parent.children[search(l.parent.children, l)] = s
		l.parent = s

		tab.curView++
		tab.views = append(tab.views, newView)
	}
}

func (l *LeafNode) Delete() {
	i := search(l.parent.children, l)

	copy(l.parent.children[i:], l.parent.children[i+1:])
	l.parent.children[len(l.parent.children)-1] = nil
	l.parent.children = l.parent.children[:len(l.parent.children)-1]

	tab := tabs[l.parent.tabNum]
	j := findView(tab.views, l.view)
	copy(tab.views[j:], tab.views[j+1:])
	tab.views[len(tab.views)-1] = nil // or the zero value of T
	tab.views = tab.views[:len(tab.views)-1]

	for i, v := range tab.views {
		v.Num = i
	}
	if tab.curView > 0 {
		tab.curView--
	}
}

func (s *SplitTree) Cleanup() {
	for i, node := range s.children {
		if n, ok := node.(*SplitTree); ok {
			if len(n.children) == 1 {
				if child, ok := n.children[0].(*LeafNode); ok {
					s.children[i] = child
					child.parent = s
					continue
				}
			}
			n.Cleanup()
		}
	}
}

func (s *SplitTree) ResizeSplits() {
	for i, node := range s.children {
		if n, ok := node.(*LeafNode); ok {
			if s.kind == VerticalSplit {
				n.view.width = s.width / len(s.children)
				n.view.height = s.height

				n.view.x = s.x + n.view.width*i
				n.view.y = s.y
			} else {
				n.view.height = s.height / len(s.children)
				n.view.width = s.width

				n.view.y = s.y + n.view.height*i
				n.view.x = s.x
			}
			// n.view.ToggleStatusLine()
			_, screenH := screen.Size()
			if settings["statusline"].(bool) || (n.view.y+n.view.height) != screenH-1 {
				n.view.height--
			}

			n.view.ToggleTabbar()
			n.view.matches = Match(n.view)
		} else if n, ok := node.(*SplitTree); ok {
			if s.kind == VerticalSplit {
				n.width = s.width / len(s.children)
				n.height = s.height

				n.x = s.x + n.width*i
				n.y = s.y
			} else {
				n.height = s.height / len(s.children)
				n.width = s.width

				n.y = s.y + n.height*i
				n.x = s.x
			}
			n.ResizeSplits()
		}
	}
}

func (l *LeafNode) String() string {
	return l.view.Buf.Name
}

func search(haystack []Node, needle Node) int {
	for i, x := range haystack {
		if x == needle {
			return i
		}
	}
	return 0
}

func findView(haystack []*View, needle *View) int {
	for i, x := range haystack {
		if x == needle {
			return i
		}
	}
	return 0
}

func (s *SplitTree) VSplit(buf *Buffer) {}
func (s *SplitTree) HSplit(buf *Buffer) {}

func (s *SplitTree) String() string {
	str := "["
	for _, child := range s.children {
		str += child.String() + ", "
	}
	return str + "]"
}
<|endoftext|>


<commit_before>package keychain

import (
	"fmt"

	"github.com/btcsuite/btcd/btcec"
)

const (
	// KeyDerivationVersion is the version of the key derivation schema
	// defined below. We use a version as this means that we'll be able to
	// accept new seed in the future and be able to discern if the software
	// is compatible with the version of the seed.
	KeyDerivationVersion = 0

	// BIP0043Purpose is the "purpose" value that we'll use for the first
	// version or our key derivation scheme. All keys are expected to be
	// derived from this purpose, then the particular coin type of the
	// chain where the keys are to be used.  Slightly adhering to BIP0043
	// allows us to not deviate too far from a widely used standard, and
	// also fits into existing implementations of the BIP's template.
	//
	// NOTE: BRICK SQUUUUUAD.
	BIP0043Purpose = 1017
)

var (
	// MaxKeyRangeScan is the maximum number of keys that we'll attempt to
	// scan with if a caller knows the public key, but not the KeyLocator
	// and wishes to derive a private key.
	MaxKeyRangeScan = 100000

	// ErrCannotDerivePrivKey is returned when DerivePrivKey is unable to
	// derive a private key given only the public key and target key
	// family.
	ErrCannotDerivePrivKey = fmt.Errorf("unable to derive private key")
)

// KeyFamily represents a "family" of keys that will be used within various
// contracts created by lnd. These families are meant to be distinct branches
// within the HD key chain of the backing wallet. Usage of key families within
// the interface below are strict in order to promote integrability and the
// ability to restore all keys given a user master seed backup.
//
// The key derivation in this file follows the following hierarchy based on
// BIP43:
//
//   * m/1017'/coinType'/keyFamily/0/index
type KeyFamily uint32

const (
	// KeyFamilyMultiSig are keys to be used within multi-sig scripts.
	KeyFamilyMultiSig KeyFamily = 0

	// KeyFamilyRevocationBase are keys that are used within channels to
	// create revocation basepoints that the remote party will use to
	// create revocation keys for us.
	KeyFamilyRevocationBase = 1

	// KeyFamilyHtlcBase are keys used within channels that will be
	// combined with per-state randomness to produce public keys that will
	// be used in HTLC scripts.
	KeyFamilyHtlcBase KeyFamily = 2

	// KeyFamilyPaymentBase are keys used within channels that will be
	// combined with per-state randomness to produce public keys that will
	// be used in scripts that pay directly to us without any delay.
	KeyFamilyPaymentBase KeyFamily = 3

	// KeyFamilyDelayBase are keys used within channels that will be
	// combined with per-state randomness to produce public keys that will
	// be used in scripts that pay to us, but require a CSV delay before we
	// can sweep the funds.
	KeyFamilyDelayBase KeyFamily = 4

	// KeyFamilyRevocationRoot is a family of keys which will be used to
	// derive the root of a revocation tree for a particular channel.
	KeyFamilyRevocationRoot KeyFamily = 5

	// KeyFamilyNodeKey is a family of keys that will be used to derive
	// keys that will be advertised on the network to represent our current
	// "identity" within the network. Peers will need our latest node key
	// in order to establish a transport session with us on the Lightning
	// p2p level (BOLT-0008).
	KeyFamilyNodeKey KeyFamily = 6

	// KeyFamilyStaticBackup is the family of keys that will be used to
	// derive keys that we use to encrypt and decrypt our set of static
	// backups. These backups may either be stored within watch towers for
	// a payment, or self stored on disk in a single file containing all
	// the static channel backups.
	KeyFamilyStaticBackup KeyFamily = 7

	// KeyFamilyTowerSession is the family of keys that will be used to
	// derive session keys when negotiating sessions with watchtowers. The
	// session keys are limited to the lifetime of the session and are used
	// to increase privacy in the watchtower protocol.
	KeyFamilyTowerSession KeyFamily = 8
)

// KeyLocator is a two-tuple that can be used to derive *any* key that has ever
// been used under the key derivation mechanisms described in this file.
// Version 0 of our key derivation schema uses the following BIP43-like
// derivation:
//
//   * m/201'/coinType'/keyFamily/0/index
//
// Our purpose is 201 (chosen arbitrary for now), and the coin type will vary
// based on which coin/chain the channels are being created on. The key family
// are actually just individual "accounts" in the nomenclature of BIP43. By
// default we assume a branch of 0 (external). Finally, the key index (which
// will vary per channel and use case) is the final element which allows us to
// deterministically derive keys.
type KeyLocator struct {
	// TODO(roasbeef): add the key scope as well??

	// Family is the family of key being identified.
	Family KeyFamily

	// Index is the precise index of the key being identified.
	Index uint32
}

// IsEmpty returns true if a KeyLocator is "empty". This may be the case where
// we learn of a key from a remote party for a contract, but don't know the
// precise details of its derivation (as we don't know the private key!).
func (k KeyLocator) IsEmpty() bool {
	return k.Family == 0 && k.Index == 0
}

// KeyDescriptor wraps a KeyLocator and also optionally includes a public key.
// Either the KeyLocator must be non-empty, or the public key pointer be
// non-nil. This will be used by the KeyRing interface to lookup arbitrary
// private keys, and also within the SignDescriptor struct to locate precisely
// which keys should be used for signing.
type KeyDescriptor struct {
	// KeyLocator is the internal KeyLocator of the descriptor.
	KeyLocator

	// PubKey is an optional public key that fully describes a target key.
	// If this is nil, the KeyLocator MUST NOT be empty.
	PubKey *btcec.PublicKey
}

// KeyRing is the primary interface that will be used to perform public
// derivation of various keys used within the peer-to-peer network, and also
// within any created contracts. All derivation required by the KeyRing is
// based off of public derivation, so a system with only an extended public key
// (for the particular purpose+family) can derive this set of keys.
type KeyRing interface {
	// DeriveNextKey attempts to derive the *next* key within the key
	// family (account in BIP43) specified. This method should return the
	// next external child within this branch.
	DeriveNextKey(keyFam KeyFamily) (KeyDescriptor, error)

	// DeriveKey attempts to derive an arbitrary key specified by the
	// passed KeyLocator. This may be used in several recovery scenarios,
	// or when manually rotating something like our current default node
	// key.
	DeriveKey(keyLoc KeyLocator) (KeyDescriptor, error)
}

// SecretKeyRing is a similar to the regular KeyRing interface, but it is also
// able to derive *private keys*. As this is a super-set of the regular
// KeyRing, we also expect the SecretKeyRing to implement the fully KeyRing
// interface. The methods in this struct may be used to extract the node key in
// order to accept inbound network connections, or to do manual signing for
// recovery purposes.
type SecretKeyRing interface {
	KeyRing

	// DerivePrivKey attempts to derive the private key that corresponds to
	// the passed key descriptor.  If the public key is set, then this
	// method will perform an in-order scan over the key set, with a max of
	// MaxKeyRangeScan keys. In order for this to work, the caller MUST set
	// the KeyFamily within the partially populated KeyLocator.
	DerivePrivKey(keyDesc KeyDescriptor) (*btcec.PrivateKey, error)

	// ScalarMult performs a scalar multiplication (ECDH-like operation)
	// between the target key descriptor and remote public key. The output
	// returned will be the sha256 of the resulting shared point serialized
	// in compressed format. If k is our private key, and P is the public
	// key, we perform the following operation:
	//
	//  sx := k*P
	//  s := sha256(sx.SerializeCompressed())
	ScalarMult(keyDesc KeyDescriptor, pubKey *btcec.PublicKey) ([]byte, error)
}

// TODO(roasbeef): extend to actually support scalar mult of key?
//  * would allow to push in initial handshake auth into interface as well
<commit_msg>keychain/derivation: add KeyFamilyTowerKey distinct from NodeID<commit_after>package keychain

import (
	"fmt"

	"github.com/btcsuite/btcd/btcec"
)

const (
	// KeyDerivationVersion is the version of the key derivation schema
	// defined below. We use a version as this means that we'll be able to
	// accept new seed in the future and be able to discern if the software
	// is compatible with the version of the seed.
	KeyDerivationVersion = 0

	// BIP0043Purpose is the "purpose" value that we'll use for the first
	// version or our key derivation scheme. All keys are expected to be
	// derived from this purpose, then the particular coin type of the
	// chain where the keys are to be used.  Slightly adhering to BIP0043
	// allows us to not deviate too far from a widely used standard, and
	// also fits into existing implementations of the BIP's template.
	//
	// NOTE: BRICK SQUUUUUAD.
	BIP0043Purpose = 1017
)

var (
	// MaxKeyRangeScan is the maximum number of keys that we'll attempt to
	// scan with if a caller knows the public key, but not the KeyLocator
	// and wishes to derive a private key.
	MaxKeyRangeScan = 100000

	// ErrCannotDerivePrivKey is returned when DerivePrivKey is unable to
	// derive a private key given only the public key and target key
	// family.
	ErrCannotDerivePrivKey = fmt.Errorf("unable to derive private key")
)

// KeyFamily represents a "family" of keys that will be used within various
// contracts created by lnd. These families are meant to be distinct branches
// within the HD key chain of the backing wallet. Usage of key families within
// the interface below are strict in order to promote integrability and the
// ability to restore all keys given a user master seed backup.
//
// The key derivation in this file follows the following hierarchy based on
// BIP43:
//
//   * m/1017'/coinType'/keyFamily/0/index
type KeyFamily uint32

const (
	// KeyFamilyMultiSig are keys to be used within multi-sig scripts.
	KeyFamilyMultiSig KeyFamily = 0

	// KeyFamilyRevocationBase are keys that are used within channels to
	// create revocation basepoints that the remote party will use to
	// create revocation keys for us.
	KeyFamilyRevocationBase = 1

	// KeyFamilyHtlcBase are keys used within channels that will be
	// combined with per-state randomness to produce public keys that will
	// be used in HTLC scripts.
	KeyFamilyHtlcBase KeyFamily = 2

	// KeyFamilyPaymentBase are keys used within channels that will be
	// combined with per-state randomness to produce public keys that will
	// be used in scripts that pay directly to us without any delay.
	KeyFamilyPaymentBase KeyFamily = 3

	// KeyFamilyDelayBase are keys used within channels that will be
	// combined with per-state randomness to produce public keys that will
	// be used in scripts that pay to us, but require a CSV delay before we
	// can sweep the funds.
	KeyFamilyDelayBase KeyFamily = 4

	// KeyFamilyRevocationRoot is a family of keys which will be used to
	// derive the root of a revocation tree for a particular channel.
	KeyFamilyRevocationRoot KeyFamily = 5

	// KeyFamilyNodeKey is a family of keys that will be used to derive
	// keys that will be advertised on the network to represent our current
	// "identity" within the network. Peers will need our latest node key
	// in order to establish a transport session with us on the Lightning
	// p2p level (BOLT-0008).
	KeyFamilyNodeKey KeyFamily = 6

	// KeyFamilyStaticBackup is the family of keys that will be used to
	// derive keys that we use to encrypt and decrypt our set of static
	// backups. These backups may either be stored within watch towers for
	// a payment, or self stored on disk in a single file containing all
	// the static channel backups.
	KeyFamilyStaticBackup KeyFamily = 7

	// KeyFamilyTowerSession is the family of keys that will be used to
	// derive session keys when negotiating sessions with watchtowers. The
	// session keys are limited to the lifetime of the session and are used
	// to increase privacy in the watchtower protocol.
	KeyFamilyTowerSession KeyFamily = 8

	// KeyFamilyTowerID is the family of keys used to derive the public key
	// of a watchtower. This made distinct from the node key to offer a form
	// of rudimentary whitelisting, i.e. via knowledge of the pubkey,
	// preventing others from having full access to the tower just as a
	// result of knowing the node key.
	KeyFamilyTowerID KeyFamily = 9
)

// KeyLocator is a two-tuple that can be used to derive *any* key that has ever
// been used under the key derivation mechanisms described in this file.
// Version 0 of our key derivation schema uses the following BIP43-like
// derivation:
//
//   * m/201'/coinType'/keyFamily/0/index
//
// Our purpose is 201 (chosen arbitrary for now), and the coin type will vary
// based on which coin/chain the channels are being created on. The key family
// are actually just individual "accounts" in the nomenclature of BIP43. By
// default we assume a branch of 0 (external). Finally, the key index (which
// will vary per channel and use case) is the final element which allows us to
// deterministically derive keys.
type KeyLocator struct {
	// TODO(roasbeef): add the key scope as well??

	// Family is the family of key being identified.
	Family KeyFamily

	// Index is the precise index of the key being identified.
	Index uint32
}

// IsEmpty returns true if a KeyLocator is "empty". This may be the case where
// we learn of a key from a remote party for a contract, but don't know the
// precise details of its derivation (as we don't know the private key!).
func (k KeyLocator) IsEmpty() bool {
	return k.Family == 0 && k.Index == 0
}

// KeyDescriptor wraps a KeyLocator and also optionally includes a public key.
// Either the KeyLocator must be non-empty, or the public key pointer be
// non-nil. This will be used by the KeyRing interface to lookup arbitrary
// private keys, and also within the SignDescriptor struct to locate precisely
// which keys should be used for signing.
type KeyDescriptor struct {
	// KeyLocator is the internal KeyLocator of the descriptor.
	KeyLocator

	// PubKey is an optional public key that fully describes a target key.
	// If this is nil, the KeyLocator MUST NOT be empty.
	PubKey *btcec.PublicKey
}

// KeyRing is the primary interface that will be used to perform public
// derivation of various keys used within the peer-to-peer network, and also
// within any created contracts. All derivation required by the KeyRing is
// based off of public derivation, so a system with only an extended public key
// (for the particular purpose+family) can derive this set of keys.
type KeyRing interface {
	// DeriveNextKey attempts to derive the *next* key within the key
	// family (account in BIP43) specified. This method should return the
	// next external child within this branch.
	DeriveNextKey(keyFam KeyFamily) (KeyDescriptor, error)

	// DeriveKey attempts to derive an arbitrary key specified by the
	// passed KeyLocator. This may be used in several recovery scenarios,
	// or when manually rotating something like our current default node
	// key.
	DeriveKey(keyLoc KeyLocator) (KeyDescriptor, error)
}

// SecretKeyRing is a similar to the regular KeyRing interface, but it is also
// able to derive *private keys*. As this is a super-set of the regular
// KeyRing, we also expect the SecretKeyRing to implement the fully KeyRing
// interface. The methods in this struct may be used to extract the node key in
// order to accept inbound network connections, or to do manual signing for
// recovery purposes.
type SecretKeyRing interface {
	KeyRing

	// DerivePrivKey attempts to derive the private key that corresponds to
	// the passed key descriptor.  If the public key is set, then this
	// method will perform an in-order scan over the key set, with a max of
	// MaxKeyRangeScan keys. In order for this to work, the caller MUST set
	// the KeyFamily within the partially populated KeyLocator.
	DerivePrivKey(keyDesc KeyDescriptor) (*btcec.PrivateKey, error)

	// ScalarMult performs a scalar multiplication (ECDH-like operation)
	// between the target key descriptor and remote public key. The output
	// returned will be the sha256 of the resulting shared point serialized
	// in compressed format. If k is our private key, and P is the public
	// key, we perform the following operation:
	//
	//  sx := k*P
	//  s := sha256(sx.SerializeCompressed())
	ScalarMult(keyDesc KeyDescriptor, pubKey *btcec.PublicKey) ([]byte, error)
}

// TODO(roasbeef): extend to actually support scalar mult of key?
//  * would allow to push in initial handshake auth into interface as well
<|endoftext|>


<commit_before>// Copyright 2016 The Upspin Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// TODO(adg,andybons): make the HTML pages prettier

package main

import (
	"fmt"
	"html/template"
	"net/http"
	"strings"

	"upspin.io/access"
	"upspin.io/client"
	"upspin.io/client/clientutil"
	"upspin.io/errors"
	"upspin.io/log"
	"upspin.io/path"
	"upspin.io/serverutil/perm"
	"upspin.io/upspin"
)

type web struct {
	cfg  upspin.Config
	cli  upspin.Client
	perm *perm.Perm
}

func newWeb(cfg upspin.Config, perm *perm.Perm) http.Handler {
	if !*enableWeb {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			http.NotFound(w, r)
		})
	}
	return &web{
		cfg:  cfg,
		cli:  client.New(cfg),
		perm: perm,
	}
}

func (s *web) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path == "/" {
		fmt.Fprintln(w, "Hello, upspin")
		return
	}

	urlName := upspin.PathName(strings.TrimPrefix(r.URL.Path, "/"))
	p, err := path.Parse(urlName)
	if err != nil {
		http.Error(w, "Parse: "+err.Error(), http.StatusBadRequest)
		return
	}

	// If the parsed path differs from the requested path, redirect.
	name := p.Path()
	if name != urlName {
		http.Redirect(w, r, "/"+string(name), http.StatusFound)
		return
	}

	// Check user for the path is a Writer for the server.
	if !s.perm.IsWriter(p.User()) {
		code := http.StatusForbidden
		http.Error(w, http.StatusText(code), code)
		return
	}

	// Lookup name.
	entry, err := s.cli.Lookup(name, true)
	switch {
	case errors.Match(errors.E(errors.NotExist), err),
		errors.Match(errors.E(errors.BrokenLink), err):
		// Handle NotExist or BrokenLink later, as response
		// depends on whether 'All' has 'list' right.
	case err != nil:
		httpError(w, err)
		return
	default:
		// Update name as we may have followed a link.
		name = entry.Name
	}

	// Get the Access file for the path.
	dir, err := s.cli.DirServer(name)
	if err != nil {
		httpError(w, err)
		return
	}
	whichAccess, err := dir.WhichAccess(name)
	if err != nil {
		httpError(w, err)
		return
	}
	// No Access file, default permission denied.
	if whichAccess == nil {
		code := http.StatusForbidden
		http.Error(w, http.StatusText(code), code)
		return
	}
	accessData, err := clientutil.ReadAll(s.cfg, whichAccess)
	if err != nil {
		httpError(w, err)
		return
	}
	acc, err := access.Parse(whichAccess.Name, accessData)
	if err != nil {
		httpError(w, err)
		return
	}
	// Check read and list rights for AllUsers.
	readable, err := acc.Can(access.AllUsers, access.Read, name, s.cli.Get)
	if err != nil {
		httpError(w, err)
		return
	}
	listable, err := acc.Can(access.AllUsers, access.List, name, s.cli.Get)
	if err != nil {
		httpError(w, err)
		return
	}

	switch {
	case entry == nil && listable:
		code := http.StatusNotFound
		http.Error(w, http.StatusText(code), code)
	case entry == nil:
		code := http.StatusForbidden
		http.Error(w, http.StatusText(code), code)
	case entry.IsDir() && listable:
		var d dirTemplateData
		d.Dir = p.Path()
		d.Content, err = s.cli.Glob(upspin.AllFilesGlob(name))
		if err != nil {
			httpError(w, err)
			return
		}
		if p.NElem() > 0 {
			d.Parent = p.Drop(1).Path()
		}
		if err := dirTemplate.Execute(w, d); err != nil {
			log.Error.Printf("rendering directory template: %v", err)
			httpError(w, err)
			return
		}
	case !entry.IsDir() && readable:
		data, err := s.cli.Get(name)
		if err != nil {
			httpError(w, err)
			return
		}
		w.Write(data)
	default:
		code := http.StatusForbidden
		http.Error(w, http.StatusText(code), code)
	}
}

// ifError checks if the error is the expected one, and if so writes back an
// HTTP error of the corresponding code.
func ifError(w http.ResponseWriter, got error, want errors.Kind, code int) bool {
	if !errors.Match(errors.E(want), got) {
		return false
	}
	http.Error(w, http.StatusText(code), code)
	return true
}

func httpError(w http.ResponseWriter, err error) {
	// This construction sets the HTTP error to the first type that matches.
	switch {
	case ifError(w, err, errors.Private, http.StatusForbidden):
	case ifError(w, err, errors.Permission, http.StatusForbidden):
	case ifError(w, err, errors.NotExist, http.StatusNotFound):
	case ifError(w, err, errors.BrokenLink, http.StatusNotFound):
	default:
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

type dirTemplateData struct {
	Dir     upspin.PathName
	Parent  upspin.PathName
	Content []*upspin.DirEntry
}

var templateFuncs = template.FuncMap{
	"shortname": func(name upspin.PathName) upspin.PathName {
		parent := path.DropPath(name, 1)
		if name == parent {
			return name
		}
		if !strings.HasSuffix(string(parent), "/") {
			parent += "/"
		}
		return upspin.PathName(strings.TrimPrefix(string(name), string(parent)))
	},
}

var dirTemplate = template.Must(template.New("dir").Funcs(templateFuncs).Parse(`

Index of {{.Dir}}

{{with .Parent}}

../

{{end}}
{{range .Content}}

{{shortname .Name}}{{if .IsDir}}/{{end}}

{{end}}

`))
<commit_msg>cmd/upspinserver: fix access lookup of broken links for web interface<commit_after>// Copyright 2016 The Upspin Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// TODO(adg,andybons): make the HTML pages prettier

package main

import (
	"fmt"
	"html/template"
	"net/http"
	"strings"

	"upspin.io/access"
	"upspin.io/client"
	"upspin.io/client/clientutil"
	"upspin.io/errors"
	"upspin.io/log"
	"upspin.io/path"
	"upspin.io/serverutil/perm"
	"upspin.io/upspin"
)

type web struct {
	cfg  upspin.Config
	cli  upspin.Client
	perm *perm.Perm
}

func newWeb(cfg upspin.Config, perm *perm.Perm) http.Handler {
	if !*enableWeb {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			http.NotFound(w, r)
		})
	}
	return &web{
		cfg:  cfg,
		cli:  client.New(cfg),
		perm: perm,
	}
}

func (s *web) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path == "/" {
		fmt.Fprintln(w, "Hello, upspin")
		return
	}

	urlName := upspin.PathName(strings.TrimPrefix(r.URL.Path, "/"))
	p, err := path.Parse(urlName)
	if err != nil {
		http.Error(w, "Parse: "+err.Error(), http.StatusBadRequest)
		return
	}

	// If the parsed path differs from the requested path, redirect.
	name := p.Path()
	if name != urlName {
		http.Redirect(w, r, "/"+string(name), http.StatusFound)
		return
	}

	// Check user for the path is a Writer for the server.
	if !s.perm.IsWriter(p.User()) {
		code := http.StatusForbidden
		http.Error(w, http.StatusText(code), code)
		return
	}

	// Lookup name.
	entry, err := s.cli.Lookup(name, true)
	switch {
	case errors.Match(errors.E(errors.NotExist), err),
		errors.Match(errors.E(errors.BrokenLink), err):
		// Handle NotExist or BrokenLink later, as response
		// depends on whether 'All' has 'list' right.
	case err != nil:
		httpError(w, err)
		return
	default:
		// Update name as we may have followed a link.
		name = entry.Name
	}

	// Fetch read and list rights for AllUsers.
	readable, listable, err := s.accessAll(name)
	if err != nil {
		httpError(w, err)
		return
	}

	switch {
	case entry == nil && listable:
		code := http.StatusNotFound
		http.Error(w, http.StatusText(code), code)
	case entry == nil:
		code := http.StatusForbidden
		http.Error(w, http.StatusText(code), code)
	case entry.IsDir() && listable:
		var d dirTemplateData
		d.Dir = p.Path()
		d.Content, err = s.cli.Glob(upspin.AllFilesGlob(name))
		if err != nil {
			httpError(w, err)
			return
		}
		if p.NElem() > 0 {
			d.Parent = p.Drop(1).Path()
		}
		if err := dirTemplate.Execute(w, d); err != nil {
			log.Error.Printf("rendering directory template: %v", err)
			httpError(w, err)
			return
		}
	case !entry.IsDir() && readable:
		data, err := s.cli.Get(name)
		if err != nil {
			httpError(w, err)
			return
		}
		w.Write(data)
	default:
		code := http.StatusForbidden
		http.Error(w, http.StatusText(code), code)
	}
}
// whichAccess returns Access entry for path name, handling links.
func (s *web) whichAccess(name upspin.PathName) (*upspin.DirEntry, error) {
	dir, err := s.cli.DirServer(name)
	if err != nil {
		return nil, err
	}
	for {
		whichAccess, err := dir.WhichAccess(name)
		if err == upspin.ErrFollowLink {
			// If we get link error, go back up the path looking for
			// the Access file that rules over the link.
			name = path.DropPath(name, 1)
			continue
		}
		if err != nil {
			return nil, err
		}
		return whichAccess, nil
	}
}

// accessAll returns read and list rights of path for AllUsers.
func (s *web) accessAll(name upspin.PathName) (bool, bool, error) {
	// Get access entry.
	whichAccess, err := s.whichAccess(name)
	if err != nil {
		return false, false, err
	}
	// No Access file, no read nor list rights for AllUsers.
	if whichAccess == nil {
		return false, false, nil
	}
	accessData, err := clientutil.ReadAll(s.cfg, whichAccess)
	if err != nil {
		return false, false, err
	}
	acc, err := access.Parse(whichAccess.Name, accessData)
	if err != nil {
		return false, false, err
	}
	// Check read and list rights for AllUsers.
	readable, err := acc.Can(access.AllUsers, access.Read, name, s.cli.Get)
	if err != nil {
		return false, false, err
	}
	listable, err := acc.Can(access.AllUsers, access.List, name, s.cli.Get)
	if err != nil {
		return false, false, err
	}

	return readable, listable, nil
}

// ifError checks if the error is the expected one, and if so writes back an
// HTTP error of the corresponding code.
func ifError(w http.ResponseWriter, got error, want errors.Kind, code int) bool {
	if !errors.Match(errors.E(want), got) {
		return false
	}
	http.Error(w, http.StatusText(code), code)
	return true
}

func httpError(w http.ResponseWriter, err error) {
	// This construction sets the HTTP error to the first type that matches.
	switch {
	case ifError(w, err, errors.Private, http.StatusForbidden):
	case ifError(w, err, errors.Permission, http.StatusForbidden):
	case ifError(w, err, errors.NotExist, http.StatusNotFound):
	case ifError(w, err, errors.BrokenLink, http.StatusNotFound):
	default:
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

type dirTemplateData struct {
	Dir     upspin.PathName
	Parent  upspin.PathName
	Content []*upspin.DirEntry
}

var templateFuncs = template.FuncMap{
	"shortname": func(name upspin.PathName) upspin.PathName {
		parent := path.DropPath(name, 1)
		if name == parent {
			return name
		}
		if !strings.HasSuffix(string(parent), "/") {
			parent += "/"
		}
		return upspin.PathName(strings.TrimPrefix(string(name), string(parent)))
	},
}

var dirTemplate = template.Must(template.New("dir").Funcs(templateFuncs).Parse(`

Index of {{.Dir}}

{{with .Parent}}

../

{{end}}
{{range .Content}}

{{shortname .Name}}{{if .IsDir}}/{{end}}

{{end}}

`))
<|endoftext|>


<commit_before>package tagit

import (
	"errors"
	"gopkg.in/mgo.v2/bson"
	"strconv"
)

// Tags struct that holds tags and have a simple interface for working with
// the tags. By composing this the list of tags will be able
// to be marshalled/unmarshalled to/from JSON.
type Tags struct {
	tags map[string]struct{}
}

// NewTags is a constructor for Tags.
// You should initialize your tags with this function.
func NewTags() *Tags {
	t := new(Tags)
	t.initTags()
	return t
}

func (t *Tags) initTags() {
	if t.tags == nil {
		t.tags = make(map[string]struct{})
	}
}

// All returns all tags as a slice of strings.
// The order of the tags in the slice may vary between different calls.
func (t *Tags) All() []string {
	t.initTags()
	return keys(t.tags)
}

// Has checks if a tags is in the list.
// Returns boolean.
func (t *Tags) Has(tag string) bool {
	t.initTags()
	_, ok := t.tags[tag]
	return ok
}

// Add adds a tag to the list.
func (t *Tags) Add(tag string) {
	t.initTags()
	t.tags[tag] = struct{}{}
}

// Remove removes a tag from the list.
func (t *Tags) Remove(tag string) {
	t.initTags()
	delete(t.tags, tag)
}

// Count counts the number of tags and return it as int.
func (t *Tags) Count() int {
	return len(t.All())
}

// String to implement fmt.Stringer.
// Return tags as comma separated list of words.
func (t *Tags) String() (res string) {
	if len(t.All()) <= 0 {
		return
	}

	for _, tag := range t.All() {
		res += ", " + tag
	}

	return res[2:]
}

func (t *Tags) quotedTags() (res string) {
	if len(t.All()) <= 0 {
		return
	}

	for _, tag := range t.All() {
		res += "," + strconv.Quote(tag)
	}

	return res[1:]
}

// MarshalJSON to implement json.Marshaller.
// Returns the tags as JSON list of strings.
func (t *Tags) MarshalJSON() ([]byte, error) {
	return []byte("[" + t.quotedTags() + "]"), nil
}

// UnmarshalJSON  to implement json.Unmarshaller.
// Expects JSON list of strings. Adds the tags to the list.
// Will not remove the current tags in the list.
func (t *Tags) UnmarshalJSON(json []byte) error {
	var quote, backSlash, leftBracket, rightBracket byte
	quote, leftBracket, backSlash, rightBracket = 34, 91, 92, 93
	if json[0] != leftBracket || json[len(json)-1] != rightBracket {
		return errors.New("The JSON is not a list")
	}
	var wordBytes []byte
	inWord := false
	for pos := 1; pos < len(json); pos++ {
		if inWord {
			if json[pos] == backSlash {
				slashesh := 1
				pos++
				for json[pos] == backSlash {
					slashesh++
					if slashesh == 2 {
						wordBytes = append(wordBytes, backSlash)
						slashesh = 0
					}
					pos++
				}
				if slashesh == 0 {
					pos--
					continue
				}
			} else if json[pos] == quote {
				inWord = false
				t.Add(string(wordBytes))
				continue
			}
			wordBytes = append(wordBytes, json[pos])
		} else {
			if json[pos] == quote {
				wordBytes = make([]byte, 0, len(json)-pos)
				inWord = true
			}
		}
	}
	return nil
}

func (t *Tags) GetBSON() (interface{}, error) {
	return t.All(), nil
}

func (t *Tags) SetBSON(raw bson.Raw) error {
	tags := make([]string, 0)
	err := raw.Unmarshal(&tags)

	if err != nil {
		return err
	}

	for _, tag := range tags {
		t.Add(tag)
	}

	return nil
}
<commit_msg>Add comments to new methods to go-lint is happy.<commit_after>package tagit

import (
	"errors"
	"gopkg.in/mgo.v2/bson"
	"strconv"
)

// Tags struct that holds tags and have a simple interface for working with
// the tags. By composing this the list of tags will be able
// to be marshalled/unmarshalled to/from JSON.
type Tags struct {
	tags map[string]struct{}
}

// NewTags is a constructor for Tags.
// You should initialize your tags with this function.
func NewTags() *Tags {
	t := new(Tags)
	t.initTags()
	return t
}

func (t *Tags) initTags() {
	if t.tags == nil {
		t.tags = make(map[string]struct{})
	}
}

// All returns all tags as a slice of strings.
// The order of the tags in the slice may vary between different calls.
func (t *Tags) All() []string {
	t.initTags()
	return keys(t.tags)
}

// Has checks if a tags is in the list.
// Returns boolean.
func (t *Tags) Has(tag string) bool {
	t.initTags()
	_, ok := t.tags[tag]
	return ok
}

// Add adds a tag to the list.
func (t *Tags) Add(tag string) {
	t.initTags()
	t.tags[tag] = struct{}{}
}

// Remove removes a tag from the list.
func (t *Tags) Remove(tag string) {
	t.initTags()
	delete(t.tags, tag)
}

// Count counts the number of tags and return it as int.
func (t *Tags) Count() int {
	return len(t.All())
}

// String to implement fmt.Stringer.
// Return tags as comma separated list of words.
func (t *Tags) String() (res string) {
	if len(t.All()) <= 0 {
		return
	}

	for _, tag := range t.All() {
		res += ", " + tag
	}

	return res[2:]
}

func (t *Tags) quotedTags() (res string) {
	if len(t.All()) <= 0 {
		return
	}

	for _, tag := range t.All() {
		res += "," + strconv.Quote(tag)
	}

	return res[1:]
}

// MarshalJSON to implement json.Marshaller.
// Returns the tags as JSON list of strings.
func (t *Tags) MarshalJSON() ([]byte, error) {
	return []byte("[" + t.quotedTags() + "]"), nil
}

// UnmarshalJSON  to implement json.Unmarshaller.
// Expects JSON list of strings. Adds the tags to the list.
// Will not remove the current tags in the list.
func (t *Tags) UnmarshalJSON(json []byte) error {
	var quote, backSlash, leftBracket, rightBracket byte
	quote, leftBracket, backSlash, rightBracket = 34, 91, 92, 93
	if json[0] != leftBracket || json[len(json)-1] != rightBracket {
		return errors.New("The JSON is not a list")
	}
	var wordBytes []byte
	inWord := false
	for pos := 1; pos < len(json); pos++ {
		if inWord {
			if json[pos] == backSlash {
				slashesh := 1
				pos++
				for json[pos] == backSlash {
					slashesh++
					if slashesh == 2 {
						wordBytes = append(wordBytes, backSlash)
						slashesh = 0
					}
					pos++
				}
				if slashesh == 0 {
					pos--
					continue
				}
			} else if json[pos] == quote {
				inWord = false
				t.Add(string(wordBytes))
				continue
			}
			wordBytes = append(wordBytes, json[pos])
		} else {
			if json[pos] == quote {
				wordBytes = make([]byte, 0, len(json)-pos)
				inWord = true
			}
		}
	}
	return nil
}

// GetBSON is actually implementing the bson.Getter
// interface so this can be Marshaled into bson.
// This may be added to the basic tagit implementation
// because it doesn't require any new imports.
func (t *Tags) GetBSON() (interface{}, error) {
	return t.All(), nil
}

// SetBSON is implementing the bson.Setter interface
// so this can be Unmarshaled from bson.
// To implement the Setter interface we need to import
// the bson package, so we can have the bson.Raw type available.
func (t *Tags) SetBSON(raw bson.Raw) error {
	tags := []string{}
	err := raw.Unmarshal(&tags)

	if err != nil {
		return err
	}

	for _, tag := range tags {
		t.Add(tag)
	}

	return nil
}
<|endoftext|>


<commit_before>package lib

import (
    "os"
    "time"

    . "gopkg.in/check.v1"
)

func (s *OssutilCommandSuite) rawUpdate(force bool, language string) (bool, error) {
    command := "update"
    var args []string
    options := OptionMapType{
        OptionForce: &force,
        OptionLanguage: &language,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    return showElapse, err
}

func (s *OssutilCommandSuite) TestUpdate(c *C) {
    showElapse, err := s.rawUpdate(false, "中文")
    c.Assert(err, IsNil)
    c.Assert(showElapse, Equals, false)

    showElapse, err = s.rawUpdate(false, "English")
    c.Assert(err, IsNil)
    c.Assert(showElapse, Equals, false)

    showElapse, err = s.rawUpdate(true, "中文")

    showElapse, err = s.rawUpdate(true, "English")

    err = updateCommand.updateVersion(Version, "中文")

    fileName := "ossutil_test_not_exist"
    err = updateCommand.rewriteLoadConfig(fileName)
    c.Assert(err, IsNil)
}

func (s *OssutilCommandSuite) TestRevertRename(c *C) {
    filePath := ".ossutil_tempf"
    renameFilePath := ".ossutil_tempr"

    s.createFile(filePath, filePath + "i", c)
    s.createFile(renameFilePath, renameFilePath + "i", c)

    updateCommand.revertRename(filePath, renameFilePath)
    _, err := os.Stat(renameFilePath)
    c.Assert(err, NotNil)

    str := s.readFile(filePath, c)
    c.Assert(str, Equals, renameFilePath + "i")

    _ = os.Remove(filePath)
    _ = os.Remove(renameFilePath)
}

func (s *OssutilCommandSuite) TestDownloadLastestBinary(c *C) {
    tempBinaryFile := ".ossutil_test_update.temp"
    err := updateCommand.getBinary(tempBinaryFile, "1.0.0.Beta")
    c.Assert(err, IsNil)

    _ = os.Remove(tempBinaryFile)
}

func (s *OssutilCommandSuite) TestAnonymousGetToFileError(c *C) {
    bucket := bucketNamePrefix + "update"
    object := "object"
    err := updateCommand.anonymousGetToFileRetry(bucket, object, object)
    c.Assert(err, NotNil)

    s.putBucket(bucket, c)
    time.Sleep(sleepTime)
    s.putObject(bucket, object, uploadFileName, c)
    fileName := "*"
    err = updateCommand.anonymousGetToFileRetry(bucket, object, fileName)
    c.Assert(err, NotNil)

    s.renameObjects(bucket, "", true, true, c)
    s.removeBucket(bucket, true, c)
    time.Sleep(sleepTime)
}
<commit_msg>modify case<commit_after>package lib

import (
    "os"
    "time"

    . "gopkg.in/check.v1"
)

func (s *OssutilCommandSuite) rawUpdate(force bool, language string) (bool, error) {
    command := "update"
    var args []string
    options := OptionMapType{
        OptionForce: &force,
        OptionLanguage: &language,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    return showElapse, err
}

func (s *OssutilCommandSuite) TestUpdate(c *C) {
    showElapse, err := s.rawUpdate(false, "中文")
    c.Assert(err, IsNil)
    c.Assert(showElapse, Equals, false)

    showElapse, err = s.rawUpdate(false, "English")
    c.Assert(err, IsNil)
    c.Assert(showElapse, Equals, false)

    showElapse, err = s.rawUpdate(true, "中文")

    showElapse, err = s.rawUpdate(true, "English")

    err = updateCommand.updateVersion(Version, "中文")

    fileName := "ossutil_test_not_exist"
    err = updateCommand.rewriteLoadConfig(fileName)
    c.Assert(err, IsNil)
}

func (s *OssutilCommandSuite) TestRevertRename(c *C) {
    filePath := ".ossutil_tempf"
    renameFilePath := ".ossutil_tempr"

    s.createFile(filePath, filePath + "i", c)
    s.createFile(renameFilePath, renameFilePath + "i", c)

    updateCommand.revertRename(filePath, renameFilePath)
    _, err := os.Stat(renameFilePath)
    c.Assert(err, NotNil)

    str := s.readFile(filePath, c)
    c.Assert(str, Equals, renameFilePath + "i")

    _ = os.Remove(filePath)
    _ = os.Remove(renameFilePath)
}

func (s *OssutilCommandSuite) TestDownloadLastestBinary(c *C) {
    tempBinaryFile := ".ossutil_test_update.temp"
    err := updateCommand.getBinary(tempBinaryFile, "1.0.0.Beta")
    c.Assert(err, IsNil)

    _ = os.Remove(tempBinaryFile)
}

func (s *OssutilCommandSuite) TestAnonymousGetToFileError(c *C) {
    bucket := bucketNamePrefix + "update"
    object := "object"
    err := updateCommand.anonymousGetToFileRetry(bucket, object, object)
    c.Assert(err, NotNil)

    s.putBucket(bucket, c)
    time.Sleep(sleepTime)
    s.putObject(bucket, object, uploadFileName, c)
    fileName := "*"
    err = updateCommand.anonymousGetToFileRetry(bucket, object, fileName)
    c.Assert(err, NotNil)

    s.removeObjects(bucket, "", true, true, c)
    s.removeBucket(bucket, true, c)
    time.Sleep(sleepTime)
}
<|endoftext|>


<commit_before>package suite

import (
	"testing"
	"github.com/stretchr/testify/assert"
)

// This suite is intended to store values to make sure that only
// testing-suite-related methods are run.  It's also a fully
// functional example of a testing suite, using setup/teardown methods
// and a helper method that is ignored by testify.  To make this look
// more like a real world example, all tests in the suite perform some
// type of assertion.
type SuiteTester struct {
	// Include our basic suite logic.
	Suite

	// Keep counts of how many times each method is run.
	SetupSuiteRunCount int
	TearDownSuiteRunCount int
	SetupTestRunCount int
	TearDownTestRunCount int
	TestOneRunCount int
	TestTwoRunCount int
	NonTestMethodRunCount int
}

// The SetupSuite method will be run by testify once, at the very
// start of the testing suite, before any tests are run.
func (suite *SuiteTester) SetupSuite() {
	suite.SetupSuiteRunCount++
}

// The TearDownSuite method will be run by testify once, at the very
// end of the testing suite, after all tests have been run.
func (suite *SuiteTester) TearDownSuite() {
	suite.TearDownSuiteRunCount++
}

// The SetupTest method will be run before every test in the suite.
func (suite *SuiteTester) SetupTest() {
	suite.SetupTestRunCount++
}

// The TearDownTest method will be run after every test in the suite.
func (suite *SuiteTester) TearDownTest() {
	suite.TearDownTestRunCount++
}

// Every method in a testing suite that begins with "Test" will be run
// as a test.  TestOne is an example of a test.  For the purposes of
// this example, we've included assertions in the tests, since most
// tests will issue assertions.
func (suite *SuiteTester) TestOne() {
	beforeCount := suite.TestOneRunCount
	suite.TestOneRunCount++
	assert.Equal(suite.T(), suite.TestOneRunCount, beforeCount + 1)
}

// TestTwo is another example of a test.
func (suite *SuiteTester) TestTwo() {
	beforeCount := suite.TestTwoRunCount
	suite.TestTwoRunCount++
	assert.NotEqual(suite.T(), suite.TestTwoRunCount, beforeCount)
}

// NonTestMethod does not begin with "Test", so it will not be run by
// testify as a test in the suite.  This is useful for creating helper
// methods for your tests.
func (suite *SuiteTester) NonTestMethod() {
	suite.NonTestMethodRunCount++
}

// Make sure that the Run function runs all of the expected methods
// within a testing suite.
func TestRunSuite(t *testing.T) {
	suiteTester := new(SuiteTester)
	Run(t, suiteTester)

	// The suite was only run once, so the SetupSuite and TearDownSuite
	// methods should have each been run only once.
	assert.Equal(t, suiteTester.SetupSuiteRunCount, 1)
	assert.Equal(t, suiteTester.TearDownSuiteRunCount, 1)

	// There are two test methods (TestOne and TestTwo), so the
	// SetupTest and TearDownTest methods (which should be run once for
	// each test) should have been run twice.
	assert.Equal(t, suiteTester.SetupTestRunCount, 2)
	assert.Equal(t, suiteTester.TearDownTestRunCount, 2)

	// Each test should have been run once.
	assert.Equal(t, suiteTester.TestOneRunCount, 1)
	assert.Equal(t, suiteTester.TestTwoRunCount, 1)

	// Methods that don't match the test method identifier shouldn't
	// have been run at all.
	assert.Equal(t, suiteTester.NonTestMethodRunCount, 0)
}
<commit_msg>Some clarification for the documentation in the example test suite.<commit_after>package suite

import (
	"testing"
	"github.com/stretchr/testify/assert"
)

// This suite is intended to store values to make sure that only
// testing-suite-related methods are run.  It's also a fully
// functional example of a testing suite, using setup/teardown methods
// and a helper method that is ignored by testify.  To make this look
// more like a real world example, all tests in the suite perform some
// type of assertion.
type SuiteTester struct {
	// Include our basic suite logic.
	Suite

	// Keep counts of how many times each method is run.
	SetupSuiteRunCount int
	TearDownSuiteRunCount int
	SetupTestRunCount int
	TearDownTestRunCount int
	TestOneRunCount int
	TestTwoRunCount int
	NonTestMethodRunCount int
}

// The SetupSuite method will be run by testify once, at the very
// start of the testing suite, before any tests are run.
func (suite *SuiteTester) SetupSuite() {
	suite.SetupSuiteRunCount++
}

// The TearDownSuite method will be run by testify once, at the very
// end of the testing suite, after all tests have been run.
func (suite *SuiteTester) TearDownSuite() {
	suite.TearDownSuiteRunCount++
}

// The SetupTest method will be run before every test in the suite.
func (suite *SuiteTester) SetupTest() {
	suite.SetupTestRunCount++
}

// The TearDownTest method will be run after every test in the suite.
func (suite *SuiteTester) TearDownTest() {
	suite.TearDownTestRunCount++
}

// Every method in a testing suite that begins with "Test" will be run
// as a test.  TestOne is an example of a test.  For the purposes of
// this example, we've included assertions in the tests, since most
// tests will issue assertions.
func (suite *SuiteTester) TestOne() {
	beforeCount := suite.TestOneRunCount
	suite.TestOneRunCount++
	assert.Equal(suite.T(), suite.TestOneRunCount, beforeCount + 1)
}

// TestTwo is another example of a test.
func (suite *SuiteTester) TestTwo() {
	beforeCount := suite.TestTwoRunCount
	suite.TestTwoRunCount++
	assert.NotEqual(suite.T(), suite.TestTwoRunCount, beforeCount)
}

// NonTestMethod does not begin with "Test", so it will not be run by
// testify as a test in the suite.  This is useful for creating helper
// methods for your tests.
func (suite *SuiteTester) NonTestMethod() {
	suite.NonTestMethodRunCount++
}

// TestRunSuite will be run by the 'go test' command, so within it, we
// can run our suite using the Run(*testing.T, TestingSuite) function.
func TestRunSuite(t *testing.T) {
	suiteTester := new(SuiteTester)
	Run(t, suiteTester)

	// Normally, the test would end here.  The following are simply
	// some assertions to ensure that the Run function is working as
	// intended - they are not part of the example.

	// The suite was only run once, so the SetupSuite and TearDownSuite
	// methods should have each been run only once.
	assert.Equal(t, suiteTester.SetupSuiteRunCount, 1)
	assert.Equal(t, suiteTester.TearDownSuiteRunCount, 1)

	// There are two test methods (TestOne and TestTwo), so the
	// SetupTest and TearDownTest methods (which should be run once for
	// each test) should have been run twice.
	assert.Equal(t, suiteTester.SetupTestRunCount, 2)
	assert.Equal(t, suiteTester.TearDownTestRunCount, 2)

	// Each test should have been run once.
	assert.Equal(t, suiteTester.TestOneRunCount, 1)
	assert.Equal(t, suiteTester.TestTwoRunCount, 1)

	// Methods that don't match the test method identifier shouldn't
	// have been run at all.
	assert.Equal(t, suiteTester.NonTestMethodRunCount, 0)
}
<|endoftext|>


<commit_before>package eport

import (
	"bytes"
	"io"
	"testing"
)

type bs []byte

type rfun func() ([]byte, error)

func newRW(from []byte) (*bytes.Reader, *bytes.Buffer) {
	return bytes.NewReader(from), new(bytes.Buffer)
}

func testRead(
	t *testing.T,
	f rfun,
	sizes []int,
	datas []bs,
	errs []error) {
	var data []byte
	var err error

	if len(sizes) != len(datas) || len(sizes) != len(errs) {
		t.Fatalf("wrong lengths: sizes=%d, datas=%d, errs=%d",
			len(sizes), len(datas), len(errs))
	}

	for i, size := range sizes {
		data, err = f()
		if err != errs[i] {
			t.Errorf("failed on %d: got error %v, should be %v", i, err, errs[i])
		}

		if size != len(data) {
			t.Errorf("failed on %d: got data (%s) size %d, should be %d",
				i, data, len(data), size)
		}

		if bytes.Compare(datas[i], data) != 0 {
			t.Errorf("failed on %d: got data %v, should be %v", i, data, datas[i])
		}
	}

	data, err = f()

	if err != io.EOF {
		t.Errorf("last error value must be io.EOF, got %v instead", err)
	} else if len(data) != 0 {
		t.Errorf("non-empty data (%v) after EOF", data)
	}
}

func testWrite(t *testing.T, p Port, w *bytes.Buffer, sizes []int, outs []bs, final bs) {
	for i, out := range outs {
		n, err := p.Write(out)
		if n != sizes[i] {
			t.Errorf("failed on %d: expected size %d, got %d", i, sizes[i], n)
		}

		if err != nil {
			t.Errorf("failed on %d: %#v", i, err)
		}
	}

	if bytes.Compare(w.Bytes(), final) != 0 {
		t.Errorf("expected %#v, got %#v", final, w.Bytes())
	}
}
<commit_msg>tests: test for ErrBadSizeLen<commit_after>package eport

import (
	"bytes"
	"io"
	"testing"
)

type bs []byte

type rfun func() ([]byte, error)

func newRW(from []byte) (*bytes.Reader, *bytes.Buffer) {
	return bytes.NewReader(from), new(bytes.Buffer)
}

func testRead(
	t *testing.T,
	f rfun,
	sizes []int,
	datas []bs,
	errs []error) {
	var data []byte
	var err error

	if len(sizes) != len(datas) || len(sizes) != len(errs) {
		t.Fatalf("wrong lengths: sizes=%d, datas=%d, errs=%d",
			len(sizes), len(datas), len(errs))
	}

	for i, size := range sizes {
		data, err = f()
		if err != errs[i] {
			t.Errorf("failed on %d: got error %v, should be %v", i, err, errs[i])
		}

		if size != len(data) {
			t.Errorf("failed on %d: got data (%s) size %d, should be %d",
				i, data, len(data), size)
		}

		if bytes.Compare(datas[i], data) != 0 {
			t.Errorf("failed on %d: got data %v, should be %v", i, data, datas[i])
		}
	}

	data, err = f()

	if err != io.EOF {
		t.Errorf("last error value must be io.EOF, got %v instead", err)
	} else if len(data) != 0 {
		t.Errorf("non-empty data (%v) after EOF", data)
	}
}

func testWrite(t *testing.T, p Port, w *bytes.Buffer, sizes []int, outs []bs, final bs) {
	for i, out := range outs {
		n, err := p.Write(out)
		if n != sizes[i] {
			t.Errorf("failed on %d: expected size %d, got %d", i, sizes[i], n)
		}

		if err != nil {
			t.Errorf("failed on %d: %#v", i, err)
		}
	}

	if bytes.Compare(w.Bytes(), final) != 0 {
		t.Errorf("expected %#v, got %#v", final, w.Bytes())
	}
}

func TestBadSizeLen(t *testing.T) {
	r, w := newRW([]byte{})
	p, err := Packet(r, w, 0)
	if p != nil {
		t.Errorf("expected nil port, got %#v", p)
	}

	if err != ErrBadSizeLen {
		t.Errorf("expected %#v, got %#v", ErrBadSizeLen, err)
	}
}
<|endoftext|>


<commit_before>package runner

import (
	"bufio"
	"context"
	"fmt"
	"io"
	"os"
	"os/exec"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"time"

	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

const EntryPattern = `[OnGameModeInit] FIRST_INIT`
const ExitPattern = `[OnScriptExit] LAST_EXIT`

var PluginPattern = regexp.MustCompile(`Loading plugin:\s(\w+)`)

func RunServer(ctx context.Context, r io.Reader, w io.Writer) {
	zap.L().Info("starting blocking process")

	// a signaller that uses logs to understand when the gamemode is restarting
	// with gmx that means this program can kill the process and restart it.
	restartKiller := make(chan struct{})

	// Pipe output to a scanner, this allows the program to read logs and
	// re-format them in a nicer format.
	outputReader, outputWriter := io.Pipe()
	go func() {
		init := true
		plugins := []string{}
		scanner := bufio.NewScanner(outputReader)
		for scanner.Scan() {
			line := scanner.Text()

			// Exit pattern triggers a full process restart.
			if strings.Contains(line, ExitPattern) {
				restartKiller <- struct{}{}
				init = true
				continue
			}

			// Entry pattern tells the scanner that the process has handed
			// control to the gamemode and preamble is finished. Log out the
			// relevant info such as plugins loaded.
			if strings.Contains(line, EntryPattern) {
				init = false
				zap.L().Info("finished initialising", zap.Strings("plugins", plugins))
			} else if init {
				match := PluginPattern.FindStringSubmatch(line)
				if len(match) == 2 {
					plugins = append(plugins, match[1])
				}

				fmt.Println(line)

				// skip unimportant log lines
				continue
			}

			// otherwise, parse log entries for the samp-logger format and write
			// them out.
			message, fields := parseSampLoggerFormat(line)
			zap.L().Info(message, fields...)
		}
	}()

	for {
		err := runBlocking(ctx, restartKiller, r, outputWriter)
		if err != nil {
			if err == context.Canceled {
				break
			}
			zap.L().Info("process exited with error, restarting", zap.Error(err))
		} else {
			zap.L().Info("process exited cleanly, restarting")
		}

		cleanup()
	}
}

func runBlocking(parentctx context.Context, restartKiller chan struct{}, in io.Reader, out io.Writer) (err error) {
	ctx, cancel := context.WithCancel(parentctx)
	defer cancel()

	var binary string
	switch runtime.GOOS {
	case "windows":
		binary = "./samp-server.exe"
	case "linux":
		binary = "./samp03svr"
	default:
		panic("unknown OS")
	}

	cmd := exec.CommandContext(ctx, binary)
	cmd.Stdin = in
	r := cmdReader(cmd)
	if err := cmd.Start(); err != nil {
		return err
	}

	go func() {
		// TODO: Handle IO copy errors.
		// nolint:errcheck
		io.Copy(out, r)
	}()

	go func() {
		<-restartKiller
		time.Sleep(time.Millisecond)
		zap.L().Info("internally triggered process restart")
		cancel()
	}()

	return cmd.Wait()
}

func cmdReader(cmd *exec.Cmd) io.Reader {
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return nil
	}
	stderr, err := cmd.StderrPipe()
	if err != nil {
		return nil
	}
	return io.MultiReader(stdout, stderr)
}

func cleanup() {
	os.Remove("server_log.txt") //nolint:errcheck
}

func parseSampLoggerFormat(line string) (string, []zapcore.Field) {
	rawFields := parseSampLoggerToMap(line)
	if len(rawFields) > 0 {
		fields := []zapcore.Field{}
		for key, value := range rawFields {
			if len(value) == 0 {
				return line, nil
			}
			if key == "text" {
				continue
			}
			fields = append(fields, transformType(key, value))
		}
		return rawFields["text"], fields
	}
	return line, nil
}

func parseSampLoggerToMap(line string) map[string]string {
	fields := make(map[string]string)

	for _, field := range splitLine(line) {
		split := strings.SplitN(field, "=", 2)
		if len(split) != 2 {
			return nil
		}
		value := strings.ReplaceAll(split[1], `\`, "")
		if value[0] == '"' {
			fields[split[0]] = strings.Trim(value, `"`)
		} else {
			fields[split[0]] = value
		}
	}

	return fields
}

func splitLine(line string) []string {
	result := []string{}
	instring := false
	escape := false
	begin := 0
	for idx, char := range line {
		if !instring {
			if char == '"' {
				instring = true
				continue
			}

			if char == ' ' {
				if begin < idx {
					result = append(result, line[begin:idx])
					begin = idx + 1
				}
				continue
			}
		}
		if instring {
			if char == '\\' {
				escape = true
				continue
			}
			if escape {
				escape = false
				continue
			}
			if char == '"' {
				instring = false
			}
		}
	}
	result = append(result, line[begin:])
	return result
}

func transformType(key, value string) zapcore.Field {
	if value[0] >= '0' && value[0] <= '9' {
		for _, c := range value {
			if c == '.' {
				if v, err := strconv.ParseFloat(value, 32); err == nil {
					return zap.Float64(key, v)
				} else {
					break
				}
			}
		}
		if v, err := strconv.ParseInt(value, 10, 32); err == nil {
			return zap.Int64(key, v)
		}
	}

	return zap.String(key, value)
}
<commit_msg>fix another crash in runner log parser<commit_after>package runner

import (
	"bufio"
	"context"
	"fmt"
	"io"
	"os"
	"os/exec"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"time"

	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

const EntryPattern = `[OnGameModeInit] FIRST_INIT`
const ExitPattern = `[OnScriptExit] LAST_EXIT`

var PluginPattern = regexp.MustCompile(`Loading plugin:\s(\w+)`)

func RunServer(ctx context.Context, r io.Reader, w io.Writer) {
	zap.L().Info("starting blocking process")

	// a signaller that uses logs to understand when the gamemode is restarting
	// with gmx that means this program can kill the process and restart it.
	restartKiller := make(chan struct{})

	// Pipe output to a scanner, this allows the program to read logs and
	// re-format them in a nicer format.
	outputReader, outputWriter := io.Pipe()
	go func() {
		init := true
		plugins := []string{}
		scanner := bufio.NewScanner(outputReader)
		for scanner.Scan() {
			line := scanner.Text()

			// Exit pattern triggers a full process restart.
			if strings.Contains(line, ExitPattern) {
				restartKiller <- struct{}{}
				init = true
				continue
			}

			// Entry pattern tells the scanner that the process has handed
			// control to the gamemode and preamble is finished. Log out the
			// relevant info such as plugins loaded.
			if strings.Contains(line, EntryPattern) {
				init = false
				zap.L().Info("finished initialising", zap.Strings("plugins", plugins))
			} else if init {
				match := PluginPattern.FindStringSubmatch(line)
				if len(match) == 2 {
					plugins = append(plugins, match[1])
				}

				fmt.Println(line)

				// skip unimportant log lines
				continue
			}

			// otherwise, parse log entries for the samp-logger format and write
			// them out.
			message, fields := parseSampLoggerFormat(line)
			zap.L().Info(message, fields...)
		}
	}()

	for {
		err := runBlocking(ctx, restartKiller, r, outputWriter)
		if err != nil {
			if err == context.Canceled {
				break
			}
			zap.L().Info("process exited with error, restarting", zap.Error(err))
		} else {
			zap.L().Info("process exited cleanly, restarting")
		}

		cleanup()
	}
}

func runBlocking(parentctx context.Context, restartKiller chan struct{}, in io.Reader, out io.Writer) (err error) {
	ctx, cancel := context.WithCancel(parentctx)
	defer cancel()

	var binary string
	switch runtime.GOOS {
	case "windows":
		binary = "./samp-server.exe"
	case "linux":
		binary = "./samp03svr"
	default:
		panic("unknown OS")
	}

	cmd := exec.CommandContext(ctx, binary)
	cmd.Stdin = in
	r := cmdReader(cmd)
	if err := cmd.Start(); err != nil {
		return err
	}

	go func() {
		// TODO: Handle IO copy errors.
		// nolint:errcheck
		io.Copy(out, r)
	}()

	go func() {
		<-restartKiller
		time.Sleep(time.Millisecond)
		zap.L().Info("internally triggered process restart")
		cancel()
	}()

	return cmd.Wait()
}

func cmdReader(cmd *exec.Cmd) io.Reader {
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return nil
	}
	stderr, err := cmd.StderrPipe()
	if err != nil {
		return nil
	}
	return io.MultiReader(stdout, stderr)
}

func cleanup() {
	os.Remove("server_log.txt") //nolint:errcheck
}

func parseSampLoggerFormat(line string) (string, []zapcore.Field) {
	rawFields := parseSampLoggerToMap(line)
	if len(rawFields) > 0 {
		fields := []zapcore.Field{}
		for key, value := range rawFields {
			if len(value) == 0 {
				return line, nil
			}
			if key == "text" {
				continue
			}
			fields = append(fields, transformType(key, value))
		}
		return rawFields["text"], fields
	}
	return line, nil
}

func parseSampLoggerToMap(line string) map[string]string {
	fields := make(map[string]string)

	for _, field := range splitLine(line) {
		split := strings.SplitN(field, "=", 2)
		if len(split) != 2 {
			return nil
		}
		value := strings.ReplaceAll(split[1], `\`, "")
		if len(value) == 0 {
			return nil
		}
		if value[0] == '"' {
			fields[split[0]] = strings.Trim(value, `"`)
		} else {
			fields[split[0]] = value
		}
	}

	return fields
}

func splitLine(line string) []string {
	result := []string{}
	instring := false
	escape := false
	begin := 0
	for idx, char := range line {
		if !instring {
			if char == '"' {
				instring = true
				continue
			}

			if char == ' ' {
				if begin < idx {
					result = append(result, line[begin:idx])
					begin = idx + 1
				}
				continue
			}
		}
		if instring {
			if char == '\\' {
				escape = true
				continue
			}
			if escape {
				escape = false
				continue
			}
			if char == '"' {
				instring = false
			}
		}
	}
	result = append(result, line[begin:])
	return result
}

func transformType(key, value string) zapcore.Field {
	if value[0] >= '0' && value[0] <= '9' {
		for _, c := range value {
			if c == '.' {
				if v, err := strconv.ParseFloat(value, 32); err == nil {
					return zap.Float64(key, v)
				} else {
					break
				}
			}
		}
		if v, err := strconv.ParseInt(value, 10, 32); err == nil {
			return zap.Int64(key, v)
		}
	}

	return zap.String(key, value)
}
<|endoftext|>


<commit_before>package es

type Request struct {
	Query  *Query `json:"query,omitempty"`
	Size   int    `json:"size,omitempty"`
	Facets `json:"facets,omitempty"`
	*Sort  `json:"sort,omitempty"`
}

func (request *Request) AddFacet(key string, facet *Facet) {
	if request.Facets == nil {
		request.Facets = Facets{}
	}
	request.Facets[key] = facet
}

type DateHistogram struct {
	Field    string `json:"field,omitempty"`
	Interval string `json:"interval,omitempty"`
}

type RequestFacet struct {
	*Terms         `json:"terms,omitempty"`
	*DateHistogram `json:"date_histogram,omitempty"`
}

type Sort map[string]*SortCriteria

type SortCriteria struct {
	Order string `json:"order,omitempty"`
}

const (
	Asc  = "asc"
	Desc = "desc"
)
<commit_msg>always send a size to elasticsearch<commit_after>package es

type Request struct {
	Query  *Query `json:"query,omitempty"`
	Size   int    `json:"size"`
	Facets `json:"facets,omitempty"`
	*Sort  `json:"sort,omitempty"`
}

func (request *Request) AddFacet(key string, facet *Facet) {
	if request.Facets == nil {
		request.Facets = Facets{}
	}
	request.Facets[key] = facet
}

type DateHistogram struct {
	Field    string `json:"field,omitempty"`
	Interval string `json:"interval,omitempty"`
}

type RequestFacet struct {
	*Terms         `json:"terms,omitempty"`
	*DateHistogram `json:"date_histogram,omitempty"`
}

type Sort map[string]*SortCriteria

type SortCriteria struct {
	Order string `json:"order,omitempty"`
}

const (
	Asc  = "asc"
	Desc = "desc"
)
<|endoftext|>


<commit_before>// Package bulk has functions to load and save image metadata to a CSV file as
// needed by the 'Bulk Add CSV' Shimmie2 extension. The CSV file is assumed to
// have the following format:
//
//  "/path/to/image.jpg","spaced tags","source","rating s/q/e","/path/thumbnail.jpg"
//
// The last record (thumbnail) is left empty as thumbnails can easily be
// generated by the server.
//
// The package assumes that all images and the CSV file are under a certain
// directory path that is used as input in many package functions.
package bulk

import (
	"encoding/csv"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"sort"
	"strings"
)

// Image holds the metadata of each image from the CSV file.
type Image struct {
	ID     int
	Name   string
	Tags   []string
	Source string
	Rating string
}

var supportedExt = []string{"gif", "jpeg", "jpg", "png", "swf"}

func isSupportedType(name string) bool {
	fname := strings.ToLower(name)
	for _, ext := range supportedExt {
		// The only possible returned error is ErrBadPattern, when pattern is
		// malformed. Patterns like *.jpg are never malformed so we ignore the
		// error.
		matches, _ := filepath.Match("*."+ext, fname)
		if matches {
			return true
		}
	}
	return false
}

// LoadImages expects a slice of directory entries (os.FileInfo) which is the
// result of a read directory like ioutil.ReadDir. It loops through the slice,
// ignoring any directory and keeps only the files with one of the following
// extensions: "gif", "jpeg", "jpg", "png", "swf"
//
// It returns a slice of images without metadata, using the filename as Name
// and the order the files were found as an increasing ID starting from 0.
//
// In case of a CSV file, the image metadata should be read using LoadCSV and
// then combined with the images (discovered by LoadImages) using Combine.
func LoadImages(files []os.FileInfo) []Image {
	images := []Image{}

	id := 0
	for _, f := range files {
		if !f.IsDir() {
			if isSupportedType(f.Name()) {
				img := Image{ID: id, Name: f.Name()}
				images = append(images, img)
				id++
			}
		}
	}
	return images
}

// LoadCSV loads the image metadata from a CSV file that is open for reading.
// The metadata are returned as slice of images and should be combined with the
// slice of images discovered by LoadImages by calling Combine.
func LoadCSV(file io.Reader) ([]Image, error) {
	images := []Image{}

	r := csv.NewReader(file)
	for {
		record, err := r.Read()
		if err == io.EOF {
			break
		}
		if err != nil {
			return nil, err
		}

		if len(record) != 5 {
			return nil, fmt.Errorf("invalid csv file format")
		}
		// Image filepath (first column) should exist otherwise we cannot match
		// the metadata with the images found under the directory.
		if record[0] != "" {
			img := Image{
				Name:   filepath.Base(record[0]),
				Tags:   strings.Split(record[1], " "),
				Source: record[2],
				Rating: record[3],
			}
			images = append(images, img)
		}
	}
	return images, nil
}

// Combine takes the metadata of imagesWithInfo and copies them to images
// returning the combined result.
func Combine(images, imagesWithInfo []Image) []Image {
	for _, info := range imagesWithInfo {
		if info.Name == "" {
			continue
		}
		img := findByName(images, info.Name)
		if img != nil {
			img.Source = info.Source
			img.Rating = info.Rating
			img.Tags = info.Tags
		}
	}
	return images
}

type byName []Image

func (img byName) Len() int           { return len(img) }
func (img byName) Swap(i, j int)      { img[i], img[j] = img[j], img[i] }
func (img byName) Less(i, j int) bool { return img[i].Name < img[j].Name }

func findByName(image []Image, name string) *Image {
	sort.Sort(byName(image))
	i := sort.Search(len(image), func(i int) bool { return image[i].Name >= name })
	if i < len(image) && image[i].Name == name {
		return &image[i]
	}
	return nil
}

type byID []Image

func (img byID) Len() int           { return len(img) }
func (img byID) Swap(i, j int)      { img[i], img[j] = img[j], img[i] }
func (img byID) Less(i, j int) bool { return img[i].ID < img[j].ID }

// FindByID takes a slice of images, sorts them by ID and then finds then one
// with ID id.
func FindByID(image []Image, id int) *Image {
	sort.Sort(byID(image))
	i := sort.Search(len(image), func(i int) bool { return image[i].ID >= id })
	if i < len(image) && image[i].ID == id {
		return &image[i]
	}
	return nil
}

// CurrentPrefix reads from an open CSV file, reads the first line and uses the
// base directory of the provided dir path to find the path prefix used in the
// CSV file.
//
// As an example if the provided dir path is '/localpath/pics' and the first line
// has '/serverpath/pics/pic1' then the returned current prefix will be
// '/serverpath'.
func CurrentPrefix(dir string, file io.Reader) (string, error) {
	r := csv.NewReader(file)
	record, err := r.Read()
	if err == io.EOF {
		return "", nil
	}
	if err != nil {
		return "", err
	}

	serverDir := record[0]
	folder := filepath.Base(dir)
	sep := fmt.Sprintf("%c", filepath.Separator)
	if !strings.Contains(serverDir, folder) {
		return sep, nil
	}
	for {
		if filepath.Base(serverDir) == folder {
			break
		} else {
			serverDir, _ = filepath.Split(serverDir)
			serverDir = filepath.Dir(serverDir)
		}
	}
	return filepath.Dir(serverDir), nil
}

// Save will write the image metadata to an open for writing file. It will
// keep the base of the dir path and replace the prefix with the provided one.
func Save(file io.Writer, images []Image, dir, prefix string, useLinuxSep bool) error {
	w := csv.NewWriter(file)
	w.WriteAll(toRecords(images, dir, prefix, useLinuxSep))

	if err := w.Error(); err != nil {
		return fmt.Errorf("error writing csv: %v", err)
	}
	return nil
}

func toRecords(images []Image, dir, prefix string, useLinuxSep bool) [][]string {
	var records [][]string
	for _, img := range images {
		record := toRecord(img, dir, prefix, useLinuxSep)
		records = append(records, record)
	}
	return records
}

func toRecord(img Image, dir, prefix string, useLinuxSep bool) []string {
	var record []string
	p := filepath.Join(prefix, filepath.Base(dir), img.Name)
	if useLinuxSep {
		p = strings.Replace(p, "\\", "/", -1)
	}
	record = append(record, p)
	record = append(record, strings.Join(img.Tags, " "))
	record = append(record, img.Source)
	record = append(record, img.Rating)
	record = append(record, "")
	return record
}
<commit_msg>Rename dir to workingDir in CurrentPrefix<commit_after>// Package bulk has functions to load and save image metadata to a CSV file as
// needed by the 'Bulk Add CSV' Shimmie2 extension. The CSV file is assumed to
// have the following format:
//
//  "/path/to/image.jpg","spaced tags","source","rating s/q/e","/path/thumbnail.jpg"
//
// The last record (thumbnail) is left empty as thumbnails can easily be
// generated by the server.
//
// The package assumes that all images and the CSV file are under a certain
// directory path that is used as input in many package functions.
package bulk

import (
	"encoding/csv"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"sort"
	"strings"
)

// Image holds the metadata of each image from the CSV file.
type Image struct {
	ID     int
	Name   string
	Tags   []string
	Source string
	Rating string
}

var supportedExt = []string{"gif", "jpeg", "jpg", "png", "swf"}

func isSupportedType(name string) bool {
	fname := strings.ToLower(name)
	for _, ext := range supportedExt {
		// The only possible returned error is ErrBadPattern, when pattern is
		// malformed. Patterns like *.jpg are never malformed so we ignore the
		// error.
		matches, _ := filepath.Match("*."+ext, fname)
		if matches {
			return true
		}
	}
	return false
}

// LoadImages expects a slice of directory entries (os.FileInfo) which is the
// result of a read directory like ioutil.ReadDir. It loops through the slice,
// ignoring any directory and keeps only the files with one of the following
// extensions: "gif", "jpeg", "jpg", "png", "swf"
//
// It returns a slice of images without metadata, using the filename as Name
// and the order the files were found as an increasing ID starting from 0.
//
// In case of a CSV file, the image metadata should be read using LoadCSV and
// then combined with the images (discovered by LoadImages) using Combine.
func LoadImages(files []os.FileInfo) []Image {
	images := []Image{}

	id := 0
	for _, f := range files {
		if !f.IsDir() {
			if isSupportedType(f.Name()) {
				img := Image{ID: id, Name: f.Name()}
				images = append(images, img)
				id++
			}
		}
	}
	return images
}

// LoadCSV loads the image metadata from a CSV file that is open for reading.
// The metadata are returned as slice of images and should be combined with the
// slice of images discovered by LoadImages by calling Combine.
func LoadCSV(file io.Reader) ([]Image, error) {
	images := []Image{}

	r := csv.NewReader(file)
	for {
		record, err := r.Read()
		if err == io.EOF {
			break
		}
		if err != nil {
			return nil, err
		}

		if len(record) != 5 {
			return nil, fmt.Errorf("invalid csv file format")
		}
		// Image filepath (first column) should exist otherwise we cannot match
		// the metadata with the images found under the directory.
		if record[0] != "" {
			img := Image{
				Name:   filepath.Base(record[0]),
				Tags:   strings.Split(record[1], " "),
				Source: record[2],
				Rating: record[3],
			}
			images = append(images, img)
		}
	}
	return images, nil
}

// Combine takes the metadata of imagesWithInfo and copies them to images
// returning the combined result.
func Combine(images, imagesWithInfo []Image) []Image {
	for _, info := range imagesWithInfo {
		if info.Name == "" {
			continue
		}
		img := findByName(images, info.Name)
		if img != nil {
			img.Source = info.Source
			img.Rating = info.Rating
			img.Tags = info.Tags
		}
	}
	return images
}

type byName []Image

func (img byName) Len() int           { return len(img) }
func (img byName) Swap(i, j int)      { img[i], img[j] = img[j], img[i] }
func (img byName) Less(i, j int) bool { return img[i].Name < img[j].Name }

func findByName(image []Image, name string) *Image {
	sort.Sort(byName(image))
	i := sort.Search(len(image), func(i int) bool { return image[i].Name >= name })
	if i < len(image) && image[i].Name == name {
		return &image[i]
	}
	return nil
}

type byID []Image

func (img byID) Len() int           { return len(img) }
func (img byID) Swap(i, j int)      { img[i], img[j] = img[j], img[i] }
func (img byID) Less(i, j int) bool { return img[i].ID < img[j].ID }

// FindByID takes a slice of images, sorts them by ID and then finds then one
// with ID id.
func FindByID(image []Image, id int) *Image {
	sort.Sort(byID(image))
	i := sort.Search(len(image), func(i int) bool { return image[i].ID >= id })
	if i < len(image) && image[i].ID == id {
		return &image[i]
	}
	return nil
}

// CurrentPrefix reads from an open CSV file, reads the first line and uses the
// base directory of the provided dir path to find the path prefix used in the
// CSV file.
//
// As an example if the provided dir path is '/localpath/pics' and the first line
// has '/serverpath/pics/pic1' then the returned current prefix will be
// '/serverpath'.
func CurrentPrefix(workingDir string, file io.Reader) (string, error) {
	r := csv.NewReader(file)
	record, err := r.Read()
	if err == io.EOF {
		return "", nil
	}
	if err != nil {
		return "", err
	}

	serverDir := record[0]
	folder := filepath.Base(workingDir)
	sep := fmt.Sprintf("%c", filepath.Separator)
	if !strings.Contains(serverDir, folder) {
		return sep, nil
	}
	for {
		if filepath.Base(serverDir) == folder {
			break
		} else {
			serverDir, _ = filepath.Split(serverDir)
			serverDir = filepath.Dir(serverDir)
		}
	}
	return filepath.Dir(serverDir), nil
}

// Save will write the image metadata to an open for writing file. It will
// keep the base of the dir path and replace the prefix with the provided one.
func Save(file io.Writer, images []Image, dir, prefix string, useLinuxSep bool) error {
	w := csv.NewWriter(file)
	w.WriteAll(toRecords(images, dir, prefix, useLinuxSep))

	if err := w.Error(); err != nil {
		return fmt.Errorf("error writing csv: %v", err)
	}
	return nil
}

func toRecords(images []Image, dir, prefix string, useLinuxSep bool) [][]string {
	var records [][]string
	for _, img := range images {
		record := toRecord(img, dir, prefix, useLinuxSep)
		records = append(records, record)
	}
	return records
}

func toRecord(img Image, dir, prefix string, useLinuxSep bool) []string {
	var record []string
	p := filepath.Join(prefix, filepath.Base(dir), img.Name)
	if useLinuxSep {
		p = strings.Replace(p, "\\", "/", -1)
	}
	record = append(record, p)
	record = append(record, strings.Join(img.Tags, " "))
	record = append(record, img.Source)
	record = append(record, img.Rating)
	record = append(record, "")
	return record
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"go/build"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"regexp"
	"strings"

	"github.com/fatih/color"
	"github.com/juju/errors"
)

type Package struct {
	Repo          string
	Version       string
	LockedVersion string

	IsSelf     bool
	IsLink     bool
	LinkTarget string
}

type BunchFile struct {
	Packages []Package
	Raw      []string
}

var commentStripRegexp = regexp.MustCompile(`#.*`)
var versionSwapRegexp = regexp.MustCompile(`^(\S+)\s*(.*)`)

func (b *BunchFile) RawIndex(repo string) (int, bool) {
	for i, packString := range b.Raw {
		parts := strings.Fields(packString)

		if len(parts) < 1 {
			continue
		}

		if parts[0] == repo {
			return i, true
		}
	}

	return 0, false
}

func (b *BunchFile) PackageIndex(repo string) (int, bool) {
	for i, pack := range b.Packages {
		if pack.Repo == repo {
			return i, true
		}
	}

	return 0, false
}

func (b *BunchFile) AddPackage(packString string) error {
	pack := parsePackage(packString)

	index, present := b.RawIndex(pack.Repo)

	if present {
		packIndex, _ := b.PackageIndex(pack.Repo)
		b.Packages[packIndex] = pack

		initialLine := b.Raw[index]

		replacementString := fmt.Sprintf("$1 %s", pack.Version)
		newLine := versionSwapRegexp.ReplaceAllString(initialLine, replacementString)

		b.Raw[index] = newLine
	} else {
		b.Packages = append(b.Packages, pack)
		b.Raw = append(b.Raw, fmt.Sprintf("%s %s", pack.Repo, pack.Version))
	}

	return nil
}

func (b *BunchFile) RemovePackage(packString string) error {
	pack := parsePackage(packString)

	index, present := b.RawIndex(pack.Repo)

	if present {
		packIndex, packPresent := b.PackageIndex(pack.Repo)

		if packPresent {
			if packIndex < len(b.Packages)-1 {
				b.Packages = append(b.Packages[:packIndex], b.Packages[packIndex+1:]...)
			} else {
				b.Packages = b.Packages[:packIndex]
			}
		}

		if index < len(b.Raw)-1 {
			b.Raw = append(b.Raw[:index], b.Raw[index+1:]...)
		} else {
			b.Raw = b.Raw[:index]
		}
	}

	return nil
}

func (b *BunchFile) Save() error {
	err := ioutil.WriteFile("Bunchfile", []byte(strings.Join(append(b.Raw, ""), "\n")), 0644)

	if err != nil {
		return errors.Trace(err)
	}

	return nil
}

func createBunchfile() *BunchFile {
	return &BunchFile{}
}

func readBunchfile() (*BunchFile, error) {
	bunchbytes, err := ioutil.ReadFile("Bunchfile")

	if err != nil {
		return &BunchFile{}, errors.Trace(err)
	}

	bunch := BunchFile{
		Raw: strings.Split(strings.TrimSpace(string(bunchbytes)), "\n"),
	}

	lockedCommits := make(map[string]string)

	if exists, _ := pathExists("Bunchfile.lock"); exists {
		lockBytes, err := ioutil.ReadFile("Bunchfile.lock")
		if err != nil {
			return &BunchFile{}, errors.Trace(err)
		}

		err = json.Unmarshal(lockBytes, &lockedCommits)
		if err != nil {
			return &BunchFile{}, errors.Trace(err)
		}
	}

	for _, line := range bunch.Raw {
		line = commentStripRegexp.ReplaceAllLiteralString(line, "")
		line = strings.TrimSpace(line)

		if line == "" {
			continue
		}

		pack := Package{}

		packageInfo := strings.SplitN(line, " ", 2)

		if len(packageInfo) < 1 {
			continue
		} else {
			pack.Repo = packageInfo[0]
		}

		if len(packageInfo) >= 2 {
			pack.Version = packageInfo[1]
		}

		if strings.HasPrefix(pack.Version, "!link") || strings.HasPrefix(pack.Version, "!self") {
			if strings.HasPrefix(pack.Version, "!self") {
				pack.IsSelf = true
			}

			pack.IsLink = true

			linkList := strings.Split(pack.Version, ":")

			if len(linkList) == 2 {
				pack.LinkTarget = linkList[1]
			} else {
				wd, err := os.Getwd()
				if err != nil {
					return &BunchFile{}, errors.Trace(err)
				}

				pack.LinkTarget = wd
			}
		}

		if lockedCommits[pack.Repo] != "" {
			pack.LockedVersion = lockedCommits[pack.Repo]
		}

		bunch.Packages = append(bunch.Packages, pack)
	}

	return &bunch, nil
}

func filterCommonBasePackages(depList []string, selfBase string) []string {
	basePackages := []string{}

	for i, dep1 := range depList {
		foundPrefix := false

		if strings.HasPrefix(dep1, selfBase) {
			continue
		}

		for j, dep2 := range depList {
			if i == j {
				continue
			}

			if strings.HasPrefix(dep1, dep2) {
				foundPrefix = true
				break
			}
		}

		if !foundPrefix {
			basePackages = append(basePackages, dep1)
		}
	}

	return basePackages
}

func generateBunchfile() error {
	bunch := BunchFile{}

	goListCommand := []string{"go", "list", "--json", "."}
	output, err := exec.Command(goListCommand[0], goListCommand[1:]...).Output()
	if err != nil {
		return errors.Trace(err)
	}

	packageInfo := GoList{}
	err = json.Unmarshal(output, &packageInfo)

	if err != nil {
		return errors.Trace(err)
	}

	err = bunch.AddPackage(fmt.Sprintf("%s@!self", packageInfo.ImportPath))
	if err != nil {
		return errors.Trace(err)
	}

	for _, dep := range filterCommonBasePackages(packageInfo.Deps, packageInfo.ImportPath) {
		// check that the package is not part of the standard library
		if exists, _ := pathExists(path.Join(build.Default.GOROOT, "src", dep)); !exists {
			err = bunch.AddPackage(dep)
			if err != nil {
				return errors.Trace(err)
			}
		}
	}

	err = bunch.Save()
	if err != nil {
		return errors.Trace(err)
	}

	color.Green("Bunchfile generated successfully")

	return nil
}
<commit_msg>Omit trailing space when no version is specified<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"go/build"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"regexp"
	"strings"

	"github.com/fatih/color"
	"github.com/juju/errors"
)

type Package struct {
	Repo          string
	Version       string
	LockedVersion string

	IsSelf     bool
	IsLink     bool
	LinkTarget string
}

type BunchFile struct {
	Packages []Package
	Raw      []string
}

var commentStripRegexp = regexp.MustCompile(`#.*`)
var versionSwapRegexp = regexp.MustCompile(`^(\S+)\s*(.*)`)

func (b *BunchFile) RawIndex(repo string) (int, bool) {
	for i, packString := range b.Raw {
		parts := strings.Fields(packString)

		if len(parts) < 1 {
			continue
		}

		if parts[0] == repo {
			return i, true
		}
	}

	return 0, false
}

func (b *BunchFile) PackageIndex(repo string) (int, bool) {
	for i, pack := range b.Packages {
		if pack.Repo == repo {
			return i, true
		}
	}

	return 0, false
}

func (b *BunchFile) AddPackage(packString string) error {
	pack := parsePackage(packString)

	index, present := b.RawIndex(pack.Repo)

	if present {
		packIndex, _ := b.PackageIndex(pack.Repo)
		b.Packages[packIndex] = pack

		initialLine := b.Raw[index]

		replacementString := fmt.Sprintf("$1 %s", pack.Version)
		newLine := versionSwapRegexp.ReplaceAllString(initialLine, replacementString)

		b.Raw[index] = newLine
	} else {
		b.Packages = append(b.Packages, pack)
		raw := []string{pack.Repo}
		if pack.Version != "" {
			raw = append(raw, pack.Version)
		}

		b.Raw = append(b.Raw, strings.Join(raw, " "))
	}

	return nil
}

func (b *BunchFile) RemovePackage(packString string) error {
	pack := parsePackage(packString)

	index, present := b.RawIndex(pack.Repo)

	if present {
		packIndex, packPresent := b.PackageIndex(pack.Repo)

		if packPresent {
			if packIndex < len(b.Packages)-1 {
				b.Packages = append(b.Packages[:packIndex], b.Packages[packIndex+1:]...)
			} else {
				b.Packages = b.Packages[:packIndex]
			}
		}

		if index < len(b.Raw)-1 {
			b.Raw = append(b.Raw[:index], b.Raw[index+1:]...)
		} else {
			b.Raw = b.Raw[:index]
		}
	}

	return nil
}

func (b *BunchFile) Save() error {
	err := ioutil.WriteFile("Bunchfile", []byte(strings.Join(append(b.Raw, ""), "\n")), 0644)

	if err != nil {
		return errors.Trace(err)
	}

	return nil
}

func createBunchfile() *BunchFile {
	return &BunchFile{}
}

func readBunchfile() (*BunchFile, error) {
	bunchbytes, err := ioutil.ReadFile("Bunchfile")

	if err != nil {
		return &BunchFile{}, errors.Trace(err)
	}

	bunch := BunchFile{
		Raw: strings.Split(strings.TrimSpace(string(bunchbytes)), "\n"),
	}

	lockedCommits := make(map[string]string)

	if exists, _ := pathExists("Bunchfile.lock"); exists {
		lockBytes, err := ioutil.ReadFile("Bunchfile.lock")
		if err != nil {
			return &BunchFile{}, errors.Trace(err)
		}

		err = json.Unmarshal(lockBytes, &lockedCommits)
		if err != nil {
			return &BunchFile{}, errors.Trace(err)
		}
	}

	for _, line := range bunch.Raw {
		line = commentStripRegexp.ReplaceAllLiteralString(line, "")
		line = strings.TrimSpace(line)

		if line == "" {
			continue
		}

		pack := Package{}

		packageInfo := strings.SplitN(line, " ", 2)

		if len(packageInfo) < 1 {
			continue
		} else {
			pack.Repo = packageInfo[0]
		}

		if len(packageInfo) >= 2 {
			pack.Version = packageInfo[1]
		}

		if strings.HasPrefix(pack.Version, "!link") || strings.HasPrefix(pack.Version, "!self") {
			if strings.HasPrefix(pack.Version, "!self") {
				pack.IsSelf = true
			}

			pack.IsLink = true

			linkList := strings.Split(pack.Version, ":")

			if len(linkList) == 2 {
				pack.LinkTarget = linkList[1]
			} else {
				wd, err := os.Getwd()
				if err != nil {
					return &BunchFile{}, errors.Trace(err)
				}

				pack.LinkTarget = wd
			}
		}

		if lockedCommits[pack.Repo] != "" {
			pack.LockedVersion = lockedCommits[pack.Repo]
		}

		bunch.Packages = append(bunch.Packages, pack)
	}

	return &bunch, nil
}

func filterCommonBasePackages(depList []string, selfBase string) []string {
	basePackages := []string{}

	for i, dep1 := range depList {
		foundPrefix := false

		if strings.HasPrefix(dep1, selfBase) {
			continue
		}

		for j, dep2 := range depList {
			if i == j {
				continue
			}

			if strings.HasPrefix(dep1, dep2) {
				foundPrefix = true
				break
			}
		}

		if !foundPrefix {
			basePackages = append(basePackages, dep1)
		}
	}

	return basePackages
}

func generateBunchfile() error {
	bunch := BunchFile{}

	goListCommand := []string{"go", "list", "--json", "."}
	output, err := exec.Command(goListCommand[0], goListCommand[1:]...).Output()
	if err != nil {
		return errors.Trace(err)
	}

	packageInfo := GoList{}
	err = json.Unmarshal(output, &packageInfo)

	if err != nil {
		return errors.Trace(err)
	}

	err = bunch.AddPackage(fmt.Sprintf("%s@!self", packageInfo.ImportPath))
	if err != nil {
		return errors.Trace(err)
	}

	for _, dep := range filterCommonBasePackages(packageInfo.Deps, packageInfo.ImportPath) {
		// check that the package is not part of the standard library
		if exists, _ := pathExists(path.Join(build.Default.GOROOT, "src", dep)); !exists {
			err = bunch.AddPackage(dep)
			if err != nil {
				return errors.Trace(err)
			}
		}
	}

	err = bunch.Save()
	if err != nil {
		return errors.Trace(err)
	}

	color.Green("Bunchfile generated successfully")

	return nil
}
<|endoftext|>


<commit_before>package runtime

import (
	"fmt"
	"github.com/dotcloud/docker/utils"
	"sync"
	"time"
)

type State struct {
	sync.RWMutex
	Running    bool
	Pid        int
	ExitCode   int
	StartedAt  time.Time
	FinishedAt time.Time
	Ghost      bool
}

// String returns a human-readable description of the state
func (s *State) String() string {
	s.RLock()
	defer s.RUnlock()

	if s.Running {
		if s.Ghost {
			return fmt.Sprintf("Ghost")
		}
		return fmt.Sprintf("Up %s", utils.HumanDuration(time.Now().UTC().Sub(s.StartedAt)))
	}
	return fmt.Sprintf("Exited (%d) %s ago", s.ExitCode, utils.HumanDuration(time.Now().UTC().Sub(s.FinishedAt)))
}

func (s *State) IsRunning() bool {
	s.RLock()
	defer s.RUnlock()

	return s.Running
}

func (s *State) IsGhost() bool {
	s.RLock()
	defer s.RUnlock()

	return s.Ghost
}

func (s *State) GetExitCode() int {
	s.RLock()
	defer s.RUnlock()

	return s.ExitCode
}

func (s *State) SetGhost(val bool) {
	s.Lock()
	defer s.Unlock()

	s.Ghost = val
}

func (s *State) SetRunning(pid int) {
	s.Lock()
	defer s.Unlock()

	s.Running = true
	s.Ghost = false
	s.ExitCode = 0
	s.Pid = pid
	s.StartedAt = time.Now().UTC()
}

func (s *State) SetStopped(exitCode int) {
	s.Lock()
	defer s.Unlock()

	s.Running = false
	s.Pid = 0
	s.FinishedAt = time.Now().UTC()
	s.ExitCode = exitCode
}
<commit_msg>Fix since time exit display when s.FinishedAt is zero<commit_after>package runtime

import (
	"fmt"
	"github.com/dotcloud/docker/utils"
	"sync"
	"time"
)

type State struct {
	sync.RWMutex
	Running    bool
	Pid        int
	ExitCode   int
	StartedAt  time.Time
	FinishedAt time.Time
	Ghost      bool
}

// String returns a human-readable description of the state
func (s *State) String() string {
	s.RLock()
	defer s.RUnlock()

	if s.Running {
		if s.Ghost {
			return fmt.Sprintf("Ghost")
		}
		return fmt.Sprintf("Up %s", utils.HumanDuration(time.Now().UTC().Sub(s.StartedAt)))
	}
	if s.FinishedAt.IsZero() {
		return ""
	}
	return fmt.Sprintf("Exited (%d) %s ago", s.ExitCode, utils.HumanDuration(time.Now().UTC().Sub(s.FinishedAt)))
}

func (s *State) IsRunning() bool {
	s.RLock()
	defer s.RUnlock()

	return s.Running
}

func (s *State) IsGhost() bool {
	s.RLock()
	defer s.RUnlock()

	return s.Ghost
}

func (s *State) GetExitCode() int {
	s.RLock()
	defer s.RUnlock()

	return s.ExitCode
}

func (s *State) SetGhost(val bool) {
	s.Lock()
	defer s.Unlock()

	s.Ghost = val
}

func (s *State) SetRunning(pid int) {
	s.Lock()
	defer s.Unlock()

	s.Running = true
	s.Ghost = false
	s.ExitCode = 0
	s.Pid = pid
	s.StartedAt = time.Now().UTC()
}

func (s *State) SetStopped(exitCode int) {
	s.Lock()
	defer s.Unlock()

	s.Running = false
	s.Pid = 0
	s.FinishedAt = time.Now().UTC()
	s.ExitCode = exitCode
}
<|endoftext|>


<commit_before>package etcd

import (
	"github.com/ccding/go-logging"
)

var logger, _ = logging.SimpleLogger("go-etcd")

func init() {
	logger.SetLevel(logging.FATAL)
}

func OpenDebug() {
	logger.SetLevel(logging.NOTSET)
}

func CloseDebug() {
	logger.SetLevel(logging.FATAL)
}
<commit_msg>update due to the modify in ccding/go-logging<commit_after>package etcd

import (
	"github.com/ccding/go-logging/logging"
)

var logger, _ = logging.SimpleLogger("go-etcd")

func init() {
	logger.SetLevel(logging.FATAL)
}

func OpenDebug() {
	logger.SetLevel(logging.NOTSET)
}

func CloseDebug() {
	logger.SetLevel(logging.FATAL)
}
<|endoftext|>


<commit_before>package eventstore

import (
	"errors"
	"time"
)

var (
	ErrInvalidVersion = errors.New("invalid aggregate version")
	ErrUsedTimestamp  = errors.New("timestamp used")
	ErrUsedKey        = errors.New("datastore key used")
)

// Responsible for creating valid Application and Domain unique Ids for Aggregates
type AggregateIdGenerater interface {
	GenerateAggregateId(uint32, uint32) (uint64, error)
}

// Responsible for persisting Events to the EventStore
type EventWriter interface {
	AppendEvent(Event) (time.Time, error)
}

// Responsible for serving Streams as queries against the EventStore
type StreamReader interface {
	LoadEventStreamByAggregate(uint32, uint32, uint64) ([]Event, error)
	LoadEventStreamByDomain(uint32, uint32) ([]Event, error)
}

/*
type SerializerEvent interface {
	Event
	ToJSON() ([]byte, error)
	FromJSON([]byte) error
}

func UnmarshalEvent(jsonEvent []byte) (event Event, err error) {
	var memento *EventMemento
	err = json.Unmarshal(jsonEvent, memento)
	if err != nil {
		return
	}
	return memento, err
}
*/
<commit_msg>any changes<commit_after>package eventstore

import (
	"errors"
	"time"
)

var (
	ErrInvalidVersion = errors.New("invalid aggregate version")
	ErrUsedTimestamp  = errors.New("timestamp used")
	ErrUsedKey        = errors.New("datastore key used")
)

type EventStoreReaderWriter interface {
	AggregateIdGenerater
	EventWriter
	StreamReader
}

// Responsible for creating valid Application and Domain unique Ids for Aggregates
type AggregateIdGenerater interface {
	GenerateAggregateId(uint32, uint32) (uint64, error)
}

// Responsible for persisting Events to the EventStore
type EventWriter interface {
	AppendEvent(Event) (time.Time, error)
}

// Responsible for serving Streams as queries against the EventStore
type StreamReader interface {
	LoadEventStreamByAggregate(uint32, uint32, uint64) ([]Event, error)
	LoadEventStreamByDomain(uint32, uint32) ([]Event, error)
}

/*
type SerializerEvent interface {
	Event
	ToJSON() ([]byte, error)
	FromJSON([]byte) error
}

func UnmarshalEvent(jsonEvent []byte) (event Event, err error) {
	var memento *EventMemento
	err = json.Unmarshal(jsonEvent, memento)
	if err != nil {
		return
	}
	return memento, err
}
*/
<|endoftext|>