<commit_before>package tree

import (
	"log"

	"github.com/satori/go.uuid"
)

type Tree struct {
	Id     uuid.UUID
	Name   string
	Type   string
	Child  *Repository
	Snap   *Repository
	Action chan *Action `json:"-"`
}

type TreeSpec struct {
	Id     string
	Name   string
	Action chan *Action
}

func New(spec TreeSpec) *Tree {
	st := new(Tree)
	st.Id, _ = uuid.FromString(spec.Id)
	st.Name = spec.Name
	st.Action = spec.Action
	st.Type = "root"
	return st
}

func (st *Tree) Begin() {
	t := st.Child.Clone()
	st.Snap = &t
	st.Snap.updateParentRecursive(st)

	newFault().Attach(
		AttachRequest{
			Root:       st.Snap,
			ParentType: st.Snap.Type,
			ParentName: st.Snap.Name,
		},
	)
}

func (st *Tree) Rollback() {
	err := st.Child.Fault.Error
	ac := st.Child.Action

	st.Child = st.Snap
	st.Snap = nil
	st.Child.setActionDeep(ac)
	st.Child.setError(err)
}

func (st *Tree) Commit() {
	st.Snap = nil
}

func (st *Tree) AttachError(err Error) {
	if st.Child != nil {
		st.Child.Fault.Error <- &err
	}
}

//
// Interface: Builder
func (st *Tree) GetID() string {
	return st.Id.String()
}

func (st *Tree) GetName() string {
	return st.Name
}

func (st *Tree) GetType() string {
	return st.Type
}

//
func (st *Tree) SetError(c chan *Error) {
	if st.Child != nil {
		st.Child.setError(c)
	}
}

func (st *Tree) GetErrors() []error {
	if st.Child != nil {
		return st.Child.getErrors()
	}
	return []error{}
}

// Interface: Receiver
func (st *Tree) Receive(r ReceiveRequest) {
	switch {
	case r.ParentType == "root" &&
		r.ParentId == st.Id.String() &&
		r.ChildType == "repository":
		st.receiveRepository(r)
	default:
		if st.Child != nil {
			st.Child.Receive(r)
		} else {
			panic("not allowed")
		}
	}
}

// Interface: Unlinker
func (st *Tree) Unlink(u UnlinkRequest) {
	switch {
	case u.ParentType == "root" &&
		(u.ParentId == st.Id.String() ||
			u.ParentName == st.Name) &&
		u.ChildType == "repository" &&
		u.ChildName == st.Child.GetName():
		st.unlinkRepository(u)
	default:
		if st.Child != nil {
			st.Child.Unlink(u)
		} else {
			panic("not allowed")
		}
	}
}

// Interface: RepositoryReceiver
func (st *Tree) receiveRepository(r ReceiveRequest) {
	switch {
	case r.ParentType == "root" &&
		r.ParentId == st.Id.String() &&
		r.ChildType == "repository":
		st.Child = r.Repository
		r.Repository.setParent(st)
		r.Repository.setAction(st.Action)
	default:
		panic("not allowed")
	}
}

// Interface: RepositoryUnlinker
func (st *Tree) unlinkRepository(u UnlinkRequest) {
	switch {
	case u.ParentType == "root" &&
		u.ParentId == st.Id.String() &&
		u.ChildType == "repository" &&
		u.ChildName == st.Child.GetName():
		st.Child = nil
	default:
		panic("not allowed")
	}
}

// Interface: Finder
func (st *Tree) Find(f FindRequest, b bool) Attacher {
	if !b {
		panic(`Tree.Find: root element can never inherit a Find request`)
	}

	res := st.Child.Find(f, false)
	if res != nil {
		return res
	}
	return st.Child.Fault
}

//
func (st *Tree) ComputeCheckInstances() {
	if st.Child == nil {
		panic(`Tree.ComputeCheckInstances: no repository registered`)
	}

	log.Printf("Tree[%s]: Action=%s, ObjectType=%s, ObjectId=%s",
		st.Name,
		`ComputeCheckInstances`,
		`tree`,
		st.Id.String(),
	)
	st.Child.ComputeCheckInstances()
	return
}

//
func (st *Tree) ClearLoadInfo() {
	if st.Child == nil {
		panic(`Tree.ClearLoadInfo: no repository registered`)
	}

	st.Child.ClearLoadInfo()
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<commit_msg>Add panic fence on tree.SetError without child<commit_after>package tree

import (
	"log"

	"github.com/satori/go.uuid"
)

type Tree struct {
	Id     uuid.UUID
	Name   string
	Type   string
	Child  *Repository
	Snap   *Repository
	Action chan *Action `json:"-"`
}

type TreeSpec struct {
	Id     string
	Name   string
	Action chan *Action
}

func New(spec TreeSpec) *Tree {
	st := new(Tree)
	st.Id, _ = uuid.FromString(spec.Id)
	st.Name = spec.Name
	st.Action = spec.Action
	st.Type = "root"
	return st
}

func (st *Tree) Begin() {
	t := st.Child.Clone()
	st.Snap = &t
	st.Snap.updateParentRecursive(st)

	newFault().Attach(
		AttachRequest{
			Root:       st.Snap,
			ParentType: st.Snap.Type,
			ParentName: st.Snap.Name,
		},
	)
}

func (st *Tree) Rollback() {
	err := st.Child.Fault.Error
	ac := st.Child.Action

	st.Child = st.Snap
	st.Snap = nil
	st.Child.setActionDeep(ac)
	st.Child.setError(err)
}

func (st *Tree) Commit() {
	st.Snap = nil
}

func (st *Tree) AttachError(err Error) {
	if st.Child != nil {
		st.Child.Fault.Error <- &err
	}
}

//
// Interface: Builder
func (st *Tree) GetID() string {
	return st.Id.String()
}

func (st *Tree) GetName() string {
	return st.Name
}

func (st *Tree) GetType() string {
	return st.Type
}

//
func (st *Tree) SetError(c chan *Error) {
	if st.Child != nil {
		st.Child.setError(c)
		return
	}
	panic(`tree.SetError called without attached child`)
}

func (st *Tree) GetErrors() []error {
	if st.Child != nil {
		return st.Child.getErrors()
	}
	return []error{}
}

// Interface: Receiver
func (st *Tree) Receive(r ReceiveRequest) {
	switch {
	case r.ParentType == "root" &&
		r.ParentId == st.Id.String() &&
		r.ChildType == "repository":
		st.receiveRepository(r)
	default:
		if st.Child != nil {
			st.Child.Receive(r)
		} else {
			panic("not allowed")
		}
	}
}

// Interface: Unlinker
func (st *Tree) Unlink(u UnlinkRequest) {
	switch {
	case u.ParentType == "root" &&
		(u.ParentId == st.Id.String() ||
			u.ParentName == st.Name) &&
		u.ChildType == "repository" &&
		u.ChildName == st.Child.GetName():
		st.unlinkRepository(u)
	default:
		if st.Child != nil {
			st.Child.Unlink(u)
		} else {
			panic("not allowed")
		}
	}
}

// Interface: RepositoryReceiver
func (st *Tree) receiveRepository(r ReceiveRequest) {
	switch {
	case r.ParentType == "root" &&
		r.ParentId == st.Id.String() &&
		r.ChildType == "repository":
		st.Child = r.Repository
		r.Repository.setParent(st)
		r.Repository.setAction(st.Action)
	default:
		panic("not allowed")
	}
}

// Interface: RepositoryUnlinker
func (st *Tree) unlinkRepository(u UnlinkRequest) {
	switch {
	case u.ParentType == "root" &&
		u.ParentId == st.Id.String() &&
		u.ChildType == "repository" &&
		u.ChildName == st.Child.GetName():
		st.Child = nil
	default:
		panic("not allowed")
	}
}

// Interface: Finder
func (st *Tree) Find(f FindRequest, b bool) Attacher {
	if !b {
		panic(`Tree.Find: root element can never inherit a Find request`)
	}

	res := st.Child.Find(f, false)
	if res != nil {
		return res
	}
	return st.Child.Fault
}

//
func (st *Tree) ComputeCheckInstances() {
	if st.Child == nil {
		panic(`Tree.ComputeCheckInstances: no repository registered`)
	}

	log.Printf("Tree[%s]: Action=%s, ObjectType=%s, ObjectId=%s",
		st.Name,
		`ComputeCheckInstances`,
		`tree`,
		st.Id.String(),
	)
	st.Child.ComputeCheckInstances()
	return
}

//
func (st *Tree) ClearLoadInfo() {
	if st.Child == nil {
		panic(`Tree.ClearLoadInfo: no repository registered`)
	}

	st.Child.ClearLoadInfo()
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<|endoftext|>


<commit_before>package trie

import (
	"bufio"
	"io"
	"log"
	"os"
	"strings"
)

// Implementation of an R-Way Trie data structure.
//
// A Trie has a root Node which is the base of the tree.
// Each subsequent Node has a letter and children, which are
// nodes that have letter values associated with them.

type Node struct {
	val      int
	children map[rune]*Node
}

type Trie struct {
	root *Node
	size int
}

const nul = 0x0

func newNode(val int) *Node {
	return &Node{
		val:      val,
		children: make(map[rune]*Node),
	}
}

func (n *Node) NewChild(r rune, val int) *Node {
	node := newNode(val)
	n.children[r] = node
	return node
}

// Returns the children of this node.
func (n Node) Children() map[rune]*Node {
	return n.children
}

func (n Node) Val() int {
	return n.val
}

// Creates a new Trie with an initialized root Node.
func CreateTrie() *Trie {
	node := newNode(0)
	t := &Trie{
		root: node,
		size: 0,
	}
	return t
}

// Returns the root node for the Trie.
func (t *Trie) Root() *Node {
	return t.root
}

// Adds the key to the Trie.
func (t *Trie) AddKey(key string) int {
	t.size++
	runes := []rune(key)
	return t.addrune(t.Root(), runes, 0)
}

func (t *Trie) AddKeysFromFile(path string) {
	file, err := os.Open(path)
	if err != nil {
		log.Fatal(err)
	}

	reader := bufio.NewReader(file)

	for {
		line, err := reader.ReadString('\n')
		if err != nil {
			if err == io.EOF {
				break
			}
			log.Fatal(err)
		}

		line = strings.TrimSuffix(line, "\n")
		t.AddKey(line)
	}
}

func (t *Trie) Keys() []string {
	return t.KeysWithPrefix("")
}

func (t Trie) KeysWithPrefix(pre string) []string {
	var keys []string

	node := t.nodeAtPath(pre)
	if node == nil {
		return keys
	}

	collect(node, []rune(pre), &keys)
	return keys
}

func (t Trie) nodeAtPath(pre string) *Node {
	runes := []rune(pre)
	return findNode(t.Root(), runes, 0)
}

func findNode(node *Node, runes []rune, d int) *Node {
	if node == nil {
		return nil
	}

	if len(runes) == 0 {
		return node
	}

	upper := len(runes)
	if d == upper {
		return node
	}

	n, ok := node.Children()[runes[d]]
	if !ok {
		return nil
	}

	d++
	return findNode(n, runes, d)
}

func (t Trie) addrune(node *Node, runes []rune, i int) int {
	if len(runes) == 0 {
		node.NewChild(nul, t.size)
		return i
	}

	r := runes[0]
	c := node.Children()

	n, ok := c[r]
	if !ok {
		n = node.NewChild(r, 0)
	}

	i++
	return t.addrune(n, runes[1:], i)
}

func collect(node *Node, pre []rune, keys *[]string) {
	if len(node.Children()) < 1 {
		return
	}

	for k, v := range node.Children() {
		if v.Val() > 0 {
			*keys = append(*keys, string(pre))
			continue
		}

		npre := append(pre, k)
		collect(v, npre, keys)
	}
}
<commit_msg>Remove extra branching<commit_after>package trie

import (
	"bufio"
	"io"
	"log"
	"os"
	"strings"
)

// Implementation of an R-Way Trie data structure.
//
// A Trie has a root Node which is the base of the tree.
// Each subsequent Node has a letter and children, which are
// nodes that have letter values associated with them.

type Node struct {
	val      int
	children map[rune]*Node
}

type Trie struct {
	root *Node
	size int
}

const nul = 0x0

func newNode(val int) *Node {
	return &Node{
		val:      val,
		children: make(map[rune]*Node),
	}
}

func (n *Node) NewChild(r rune, val int) *Node {
	node := newNode(val)
	n.children[r] = node
	return node
}

// Returns the children of this node.
func (n Node) Children() map[rune]*Node {
	return n.children
}

func (n Node) Val() int {
	return n.val
}

// Creates a new Trie with an initialized root Node.
func CreateTrie() *Trie {
	node := newNode(0)
	t := &Trie{
		root: node,
		size: 0,
	}
	return t
}

// Returns the root node for the Trie.
func (t *Trie) Root() *Node {
	return t.root
}

// Adds the key to the Trie.
func (t *Trie) AddKey(key string) int {
	t.size++
	runes := []rune(key)
	return t.addrune(t.Root(), runes, 0)
}

func (t *Trie) AddKeysFromFile(path string) {
	file, err := os.Open(path)
	if err != nil {
		log.Fatal(err)
	}

	reader := bufio.NewReader(file)

	for {
		line, err := reader.ReadString('\n')
		if err != nil {
			if err == io.EOF {
				break
			}
			log.Fatal(err)
		}

		line = strings.TrimSuffix(line, "\n")
		t.AddKey(line)
	}
}

func (t *Trie) Keys() []string {
	return t.KeysWithPrefix("")
}

func (t Trie) KeysWithPrefix(pre string) []string {
	var keys []string

	node := t.nodeAtPath(pre)
	if node == nil {
		return keys
	}

	collect(node, []rune(pre), &keys)
	return keys
}

func (t Trie) nodeAtPath(pre string) *Node {
	runes := []rune(pre)
	return findNode(t.Root(), runes, 0)
}

func findNode(node *Node, runes []rune, d int) *Node {
	if node == nil {
		return nil
	}

	if len(runes) == 0 {
		return node
	}

	upper := len(runes)
	if d == upper {
		return node
	}

	n, ok := node.Children()[runes[d]]
	if !ok {
		return nil
	}

	d++
	return findNode(n, runes, d)
}

func (t Trie) addrune(node *Node, runes []rune, i int) int {
	if len(runes) == 0 {
		node.NewChild(nul, t.size)
		return i
	}

	r := runes[0]
	c := node.Children()

	n, ok := c[r]
	if !ok {
		n = node.NewChild(r, 0)
	}

	i++
	return t.addrune(n, runes[1:], i)
}

func collect(node *Node, pre []rune, keys *[]string) {
	for k, v := range node.Children() {
		if v.Val() > 0 {
			*keys = append(*keys, string(pre))
			continue
		}

		npre := append(pre, k)
		collect(v, npre, keys)
	}
}
<|endoftext|>


<commit_before>package keeper

import (
	"context"

	sdk "github.com/cosmos/cosmos-sdk/types"
	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
	sdkstakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
	"github.com/zigbee-alliance/distributed-compliance-ledger/x/validator/types"
)

func (k msgServer) DisableValidator(goCtx context.Context, msg *types.MsgDisableValidator) (*types.MsgDisableValidatorResponse, error) {
	ctx := sdk.UnwrapSDKContext(goCtx)

	creatorAddr, err := sdk.ValAddressFromBech32(msg.Creator)
	if err != nil {
		return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "Invalid Address: (%s)", err)
	}

	// check if message creator has enough rights to propose disable validator
	if !k.dclauthKeeper.HasRole(ctx, sdk.AccAddress(creatorAddr), types.EnableDisableValidatorRole) {
		return nil, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized,
			"Disable validator transaction should be signed by an account with the %s role",
			types.EnableDisableValidatorRole,
		)
	}

	// check if validator exists
	isFound := k.Keeper.IsValidatorPresent(ctx, creatorAddr)
	if !isFound {
		return nil, sdkstakingtypes.ErrNoValidatorFound
	}

	// check if disabled validator exists
	_, isFound = k.GetDisabledValidator(ctx, msg.Creator)
	if isFound {
		return nil, types.NewErrDisabledValidatorAlreadyExists(msg.Creator)
	}

	disabledValidator := types.DisabledValidator{
		Address:             msg.Creator,
		Creator:             msg.Creator,
		DisabledByNodeAdmin: true,
	}

	// Disable validator
	validator, _ := k.GetValidator(ctx, sdk.ValAddress(msg.Creator))
	k.Jail(ctx, validator, "disabled by a node admin")

	// store disabled validator
	k.SetDisabledValidator(ctx, disabledValidator)

	return &types.MsgDisableValidatorResponse{}, nil
}
<commit_msg>Add implementation for conver creator address to sdk.ValAddressFromBech32<commit_after>package keeper

import (
	"context"

	sdk "github.com/cosmos/cosmos-sdk/types"
	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
	sdkstakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
	"github.com/zigbee-alliance/distributed-compliance-ledger/x/validator/types"
)

func (k msgServer) DisableValidator(goCtx context.Context, msg *types.MsgDisableValidator) (*types.MsgDisableValidatorResponse, error) {
	ctx := sdk.UnwrapSDKContext(goCtx)

	creatorAddr, err := sdk.ValAddressFromBech32(msg.Creator)
	if err != nil {
		return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "Invalid Address: (%s)", err)
	}

	// check if message creator has enough rights to propose disable validator
	if !k.dclauthKeeper.HasRole(ctx, sdk.AccAddress(creatorAddr), types.EnableDisableValidatorRole) {
		return nil, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized,
			"Disable validator transaction should be signed by an account with the %s role",
			types.EnableDisableValidatorRole,
		)
	}

	// check if validator exists
	isFound := k.Keeper.IsValidatorPresent(ctx, creatorAddr)
	if !isFound {
		return nil, sdkstakingtypes.ErrNoValidatorFound
	}

	// check if disabled validator exists
	_, isFound = k.GetDisabledValidator(ctx, msg.Creator)
	if isFound {
		return nil, types.NewErrDisabledValidatorAlreadyExists(msg.Creator)
	}

	creator, err := sdk.ValAddressFromBech32(msg.Creator)
	if err != nil {
		return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress,
			"Don't convert validator address to ValAddress")
	}

	disabledValidator := types.DisabledValidator{
		Address:             msg.Creator,
		Creator:             string(creator),
		DisabledByNodeAdmin: true,
	}

	// Disable validator
	validator, _ := k.GetValidator(ctx, sdk.ValAddress(msg.Creator))
	k.Jail(ctx, validator, "disabled by a node admin")

	// store disabled validator
	k.SetDisabledValidator(ctx, disabledValidator)

	return &types.MsgDisableValidatorResponse{}, nil
}
<|endoftext|>


<commit_before>// TRANSACTION SIGNATURE (TSIG)
//
// A TSIG or transaction signature adds a HMAC TSIG record to each message sent.
// Basic use pattern when querying with TSIG:
//
//	m := new(Msg)
//	c := NewClient()
//	m.SetQuestion("miek.nl.", TypeMX)
//	// Set the secret under the name "axfr."
//	c.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="}	// don't forget the .
//	// Add the stub TSIG RR to the message
//	m.SetTsig("axfr.", HmacMD5, 300, uint64(time.Seconds()))
//	...
//	// When sending the TSIG RR is calculated and filled in before sending
//
// The supported algorithm include: HmacMD5, HmacSHA1 and HmacSHA256.
//
// AXFR (TODO)
// The message requesting an AXFR (almost all TSIG usage is when requesting zone transfers)
// for miek.nl with the TSIG record added is now ready to use.
// We now need a new client with access to the secrets:
//
//      c := NewClient()
//      c.TsigSecret = secrets
//      err := c.XfrReceive(m, "85.223.71.124:53")
//
// You can now read the records from the AXFR as they come in. Each envelope is checked with TSIG.
// If something is not correct an error is returned.
//
// Basic use pattern validating and replying to a message that has TSIG set.
// TODO(mg)
//
package dns

import (
	"crypto/hmac"
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"encoding/hex"
	"hash"
	"io"
	"strings"
	"time"
)

// HMAC hashing codes. These are transmitted as domain names.
const (
	HmacMD5    = "hmac-md5.sig-alg.reg.int."
	HmacSHA1   = "hmac-sha1."
	HmacSHA256 = "hmac-sha256."
)

// The following values must be put in wireformat, so that the MAC can be calculated.
// RFC 2845, section 3.4.2. TSIG Variables.
type tsigWireFmt struct {
	// From RR_Header
	Name  string "domain-name"
	Class uint16
	Ttl   uint32
	// Rdata of the TSIG
	Algorithm  string "domain-name"
	TimeSigned uint64
	Fudge      uint16
	// MACSize, MAC and OrigId excluded
	Error     uint16
	OtherLen  uint16
	OtherData string "size-hex"
}

// If we have the MAC use this type to convert it to wiredata.
// Section 3.4.3. Request MAC
type macWireFmt struct {
	MACSize uint16
	MAC     string "size-hex"
}

// 3.3. Time values used in TSIG calculations
type timerWireFmt struct {
	TimeSigned uint64
	Fudge      uint16
}

// TsigGenerate fills out the TSIG record attached to the message.
// The message should contain
// a "stub" TSIG RR with the algorithm, key name (owner name of the RR),
// time fudge (defaults to 300 seconds) and the current time
// The TSIG MAC is saved in that Tsig RR.
// When TsigGenerate is called for the first time requestMAC is set to the empty string and
// timersOnly is false.
// If something goes wrong an error is returned, otherwise it is nil.
func TsigGenerate(m *Msg, secret, requestMAC string, timersOnly bool) ([]byte, string, error) {
	if !m.IsTsig() {
		panic("TSIG not last RR in additional")
	}
	// If we barf here, the caller is to blame
	rawsecret, err := packBase64([]byte(secret))
	if err != nil {
		return nil, "", err
	}

	rr := m.Extra[len(m.Extra)-1].(*RR_TSIG)
	m.Extra = m.Extra[0 : len(m.Extra)-1] // kill the TSIG from the msg
	mbuf, ok := m.Pack()
	if !ok {
		return nil, "", ErrPack
	}
	buf := tsigBuffer(mbuf, rr, requestMAC, timersOnly)

	t := new(RR_TSIG)
	var h hash.Hash
	switch rr.Algorithm {
	case HmacMD5:
		h = hmac.New(md5.New, []byte(rawsecret))
	case HmacSHA1:
		h = hmac.New(sha1.New, []byte(rawsecret))
	case HmacSHA256:
		h = hmac.New(sha256.New, []byte(rawsecret))
	default:
		return nil, "", ErrKeyAlg
	}
	io.WriteString(h, string(buf))
	t.MAC = hex.EncodeToString(h.Sum(nil))
	t.MACSize = uint16(len(t.MAC) / 2) // Size is half!

	t.Hdr = RR_Header{Name: rr.Hdr.Name, Rrtype: TypeTSIG, Class: ClassANY, Ttl: 0}
	t.Fudge = rr.Fudge
	t.TimeSigned = rr.TimeSigned
	t.Algorithm = rr.Algorithm
	t.OrigId = m.MsgHdr.Id

	tbuf := make([]byte, t.Len())
	if off, ok := packRR(t, tbuf, 0, nil, false); ok {
		tbuf = tbuf[:off] // reset to actual size used
	} else {
		return nil, "", ErrPack
	}
	mbuf = append(mbuf, tbuf...)
	RawSetExtraLen(mbuf, uint16(len(m.Extra)+1))
	return mbuf, t.MAC, nil
}

// TsigVerify verifies the TSIG on a message.
// If the signature does not validate err contains the
// error, otherwise it is nil.
func TsigVerify(msg []byte, secret, requestMAC string, timersOnly bool) error {
	rawsecret, err := packBase64([]byte(secret))
	if err != nil {
		return err
	}
	// Srtip the TSIG from the incoming msg
	stripped, tsig, err := stripTsig(msg)
	if err != nil {
		return err
	}

	buf := tsigBuffer(stripped, tsig, requestMAC, timersOnly)

	ti := uint64(time.Now().Unix()) - tsig.TimeSigned
	if uint64(tsig.Fudge) < ti {
		return ErrTime
	}

	var h hash.Hash
	switch tsig.Algorithm {
	case HmacMD5:
		h = hmac.New(md5.New, []byte(rawsecret))
	case HmacSHA1:
		h = hmac.New(sha1.New, []byte(rawsecret))
	case HmacSHA256:
		h = hmac.New(sha256.New, []byte(rawsecret))
	default:
		return ErrKeyAlg
	}
	io.WriteString(h, string(buf))
	if strings.ToUpper(hex.EncodeToString(h.Sum(nil))) != strings.ToUpper(tsig.MAC) {
		return ErrSig
	}
	return nil
}

// Create a wiredata buffer for the MAC calculation.
func tsigBuffer(msgbuf []byte, rr *RR_TSIG, requestMAC string, timersOnly bool) []byte {
	var (
		macbuf []byte
		buf    []byte
	)
	if rr.TimeSigned == 0 {
		rr.TimeSigned = uint64(time.Now().Unix())
	}
	if rr.Fudge == 0 {
		rr.Fudge = 300 // Standard (RFC) default.
	}

	if requestMAC != "" {
		m := new(macWireFmt)
		m.MACSize = uint16(len(requestMAC) / 2)
		m.MAC = requestMAC
		macbuf = make([]byte, len(requestMAC)) // reqmac should be twice as long
		n, _ := packStruct(m, macbuf, 0)
		macbuf = macbuf[:n]
	}

	tsigvar := make([]byte, DefaultMsgSize)
	if timersOnly {
		tsig := new(timerWireFmt)
		tsig.TimeSigned = rr.TimeSigned
		tsig.Fudge = rr.Fudge
		n, _ := packStruct(tsig, tsigvar, 0)
		tsigvar = tsigvar[:n]
	} else {
		tsig := new(tsigWireFmt)
		tsig.Name = strings.ToLower(rr.Hdr.Name)
		tsig.Class = ClassANY
		tsig.Ttl = rr.Hdr.Ttl
		tsig.Algorithm = strings.ToLower(rr.Algorithm)
		tsig.TimeSigned = rr.TimeSigned
		tsig.Fudge = rr.Fudge
		tsig.Error = rr.Error
		tsig.OtherLen = rr.OtherLen
		tsig.OtherData = rr.OtherData
		n, _ := packStruct(tsig, tsigvar, 0)
		tsigvar = tsigvar[:n]
	}
	if rr.MAC != "" {
		x := append(macbuf, msgbuf...)
		buf = append(x, tsigvar...)
	} else {
		buf = append(msgbuf, tsigvar...)
	}
	return buf
}

// Strip the TSIG from the raw message
func stripTsig(msg []byte) ([]byte, *RR_TSIG, error) {
	// Copied from msg.go's Unpack()
	// Header.
	var dh Header
	dns := new(Msg)
	rr := new(RR_TSIG)
	off := 0
	tsigoff := 0
	var ok bool
	if off, ok = unpackStruct(&dh, msg, off); !ok {
		return nil, nil, ErrUnpack
	}
	if dh.Arcount == 0 {
		return nil, nil, ErrNoSig
	}
	// Rcode, see msg.go Unpack()
	if int(dh.Bits&0xF) == RcodeNotAuth {
		return nil, nil, ErrAuth
	}

	// Arrays.
	dns.Question = make([]Question, dh.Qdcount)
	dns.Answer = make([]RR, dh.Ancount)
	dns.Ns = make([]RR, dh.Nscount)
	dns.Extra = make([]RR, dh.Arcount)

	for i := 0; i < len(dns.Question); i++ {
		off, ok = unpackStruct(&dns.Question[i], msg, off)
	}
	for i := 0; i < len(dns.Answer); i++ {
		dns.Answer[i], off, ok = unpackRR(msg, off)
	}
	for i := 0; i < len(dns.Ns); i++ {
		dns.Ns[i], off, ok = unpackRR(msg, off)
	}
	for i := 0; i < len(dns.Extra); i++ {
		tsigoff = off
		dns.Extra[i], off, ok = unpackRR(msg, off)
		if dns.Extra[i].Header().Rrtype == TypeTSIG {
			rr = dns.Extra[i].(*RR_TSIG)
			// Adjust Arcount.
			arcount, _ := unpackUint16(msg, 10)
			msg[10], msg[11] = packUint16(arcount - 1)
			break
		}
	}
	if !ok {
		return nil, nil, ErrUnpack
	}
	if rr == nil {
		return nil, nil, ErrNoSig
	}
	return msg[:tsigoff], rr, nil
}
<commit_msg>documentation tsig<commit_after>// TRANSACTION SIGNATURE (TSIG)
//
// An TSIG or transaction signature adds a HMAC TSIG record to each message sent.
// Basic use pattern when querying with TSIG:
//
//	m := new(Msg)
//	c := NewClient()
//	m.SetQuestion("miek.nl.", TypeMX)
//	// Set the secret under the name "axfr."
//	c.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="}	// don't forget the .
//	// Add the stub TSIG RR to the message
//	m.SetTsig("axfr.", HmacMD5, 300, uint64(time.Seconds()))
//	...
//	// When sending the TSIG RR is calculated and filled in before sending
//
// The supported algorithm include: HmacMD5, HmacSHA1 and HmacSHA256.
//
// When requesting an AXFR (almost all TSIG usage is when requesting zone transfers), with
// TSIG, this is the basic use pattern. In this example we request an AXFR:
// * for miek.nl.
// * with TSIG key named "axfr." and secret "so6ZGir4GPAqINNh9U5c3A=="
// * use the server 85.223.71.124
//
//	c := NewClient()
//	m := New(Msg)
//	m.SetAxfr("miek.nl.")
//	c.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="}
//	err := c.XfrReceive(m, "85.223.71.124:53")
//
// You can now read the records from the AXFR as they come in. Each envelope is checked with TSIG.
// If something is not correct an error is returned.
//
// Basic use pattern validating and replying to a message that has TSIG set.
// TODO(mg)
//
//
package dns

import (
	"crypto/hmac"
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"encoding/hex"
	"hash"
	"io"
	"strings"
	"time"
)

// HMAC hashing codes. These are transmitted as domain names.
const (
	HmacMD5    = "hmac-md5.sig-alg.reg.int."
	HmacSHA1   = "hmac-sha1."
	HmacSHA256 = "hmac-sha256."
)

// The following values must be put in wireformat, so that the MAC can be calculated.
// RFC 2845, section 3.4.2. TSIG Variables.
type tsigWireFmt struct {
	// From RR_Header
	Name  string "domain-name"
	Class uint16
	Ttl   uint32
	// Rdata of the TSIG
	Algorithm  string "domain-name"
	TimeSigned uint64
	Fudge      uint16
	// MACSize, MAC and OrigId excluded
	Error     uint16
	OtherLen  uint16
	OtherData string "size-hex"
}

// If we have the MAC use this type to convert it to wiredata.
// Section 3.4.3. Request MAC
type macWireFmt struct {
	MACSize uint16
	MAC     string "size-hex"
}

// 3.3. Time values used in TSIG calculations
type timerWireFmt struct {
	TimeSigned uint64
	Fudge      uint16
}

// TsigGenerate fills out the TSIG record attached to the message.
// The message should contain
// a "stub" TSIG RR with the algorithm, key name (owner name of the RR),
// time fudge (defaults to 300 seconds) and the current time
// The TSIG MAC is saved in that Tsig RR.
// When TsigGenerate is called for the first time requestMAC is set to the empty string and
// timersOnly is false.
// If something goes wrong an error is returned, otherwise it is nil.
func TsigGenerate(m *Msg, secret, requestMAC string, timersOnly bool) ([]byte, string, error) {
	if !m.IsTsig() {
		panic("TSIG not last RR in additional")
	}
	// If we barf here, the caller is to blame
	rawsecret, err := packBase64([]byte(secret))
	if err != nil {
		return nil, "", err
	}

	rr := m.Extra[len(m.Extra)-1].(*RR_TSIG)
	m.Extra = m.Extra[0 : len(m.Extra)-1] // kill the TSIG from the msg
	mbuf, ok := m.Pack()
	if !ok {
		return nil, "", ErrPack
	}
	buf := tsigBuffer(mbuf, rr, requestMAC, timersOnly)

	t := new(RR_TSIG)
	var h hash.Hash
	switch rr.Algorithm {
	case HmacMD5:
		h = hmac.New(md5.New, []byte(rawsecret))
	case HmacSHA1:
		h = hmac.New(sha1.New, []byte(rawsecret))
	case HmacSHA256:
		h = hmac.New(sha256.New, []byte(rawsecret))
	default:
		return nil, "", ErrKeyAlg
	}
	io.WriteString(h, string(buf))
	t.MAC = hex.EncodeToString(h.Sum(nil))
	t.MACSize = uint16(len(t.MAC) / 2) // Size is half!

	t.Hdr = RR_Header{Name: rr.Hdr.Name, Rrtype: TypeTSIG, Class: ClassANY, Ttl: 0}
	t.Fudge = rr.Fudge
	t.TimeSigned = rr.TimeSigned
	t.Algorithm = rr.Algorithm
	t.OrigId = m.MsgHdr.Id

	tbuf := make([]byte, t.Len())
	if off, ok := packRR(t, tbuf, 0, nil, false); ok {
		tbuf = tbuf[:off] // reset to actual size used
	} else {
		return nil, "", ErrPack
	}
	mbuf = append(mbuf, tbuf...)
	RawSetExtraLen(mbuf, uint16(len(m.Extra)+1))
	return mbuf, t.MAC, nil
}

// TsigVerify verifies the TSIG on a message.
// If the signature does not validate err contains the
// error, otherwise it is nil.
func TsigVerify(msg []byte, secret, requestMAC string, timersOnly bool) error {
	rawsecret, err := packBase64([]byte(secret))
	if err != nil {
		return err
	}
	// Srtip the TSIG from the incoming msg
	stripped, tsig, err := stripTsig(msg)
	if err != nil {
		return err
	}

	buf := tsigBuffer(stripped, tsig, requestMAC, timersOnly)

	ti := uint64(time.Now().Unix()) - tsig.TimeSigned
	if uint64(tsig.Fudge) < ti {
		return ErrTime
	}

	var h hash.Hash
	switch tsig.Algorithm {
	case HmacMD5:
		h = hmac.New(md5.New, []byte(rawsecret))
	case HmacSHA1:
		h = hmac.New(sha1.New, []byte(rawsecret))
	case HmacSHA256:
		h = hmac.New(sha256.New, []byte(rawsecret))
	default:
		return ErrKeyAlg
	}
	io.WriteString(h, string(buf))
	if strings.ToUpper(hex.EncodeToString(h.Sum(nil))) != strings.ToUpper(tsig.MAC) {
		return ErrSig
	}
	return nil
}

// Create a wiredata buffer for the MAC calculation.
func tsigBuffer(msgbuf []byte, rr *RR_TSIG, requestMAC string, timersOnly bool) []byte {
	var (
		macbuf []byte
		buf    []byte
	)
	if rr.TimeSigned == 0 {
		rr.TimeSigned = uint64(time.Now().Unix())
	}
	if rr.Fudge == 0 {
		rr.Fudge = 300 // Standard (RFC) default.
	}

	if requestMAC != "" {
		m := new(macWireFmt)
		m.MACSize = uint16(len(requestMAC) / 2)
		m.MAC = requestMAC
		macbuf = make([]byte, len(requestMAC)) // reqmac should be twice as long
		n, _ := packStruct(m, macbuf, 0)
		macbuf = macbuf[:n]
	}

	tsigvar := make([]byte, DefaultMsgSize)
	if timersOnly {
		tsig := new(timerWireFmt)
		tsig.TimeSigned = rr.TimeSigned
		tsig.Fudge = rr.Fudge
		n, _ := packStruct(tsig, tsigvar, 0)
		tsigvar = tsigvar[:n]
	} else {
		tsig := new(tsigWireFmt)
		tsig.Name = strings.ToLower(rr.Hdr.Name)
		tsig.Class = ClassANY
		tsig.Ttl = rr.Hdr.Ttl
		tsig.Algorithm = strings.ToLower(rr.Algorithm)
		tsig.TimeSigned = rr.TimeSigned
		tsig.Fudge = rr.Fudge
		tsig.Error = rr.Error
		tsig.OtherLen = rr.OtherLen
		tsig.OtherData = rr.OtherData
		n, _ := packStruct(tsig, tsigvar, 0)
		tsigvar = tsigvar[:n]
	}
	if rr.MAC != "" {
		x := append(macbuf, msgbuf...)
		buf = append(x, tsigvar...)
	} else {
		buf = append(msgbuf, tsigvar...)
	}
	return buf
}

// Strip the TSIG from the raw message
func stripTsig(msg []byte) ([]byte, *RR_TSIG, error) {
	// Copied from msg.go's Unpack()
	// Header.
	var dh Header
	dns := new(Msg)
	rr := new(RR_TSIG)
	off := 0
	tsigoff := 0
	var ok bool
	if off, ok = unpackStruct(&dh, msg, off); !ok {
		return nil, nil, ErrUnpack
	}
	if dh.Arcount == 0 {
		return nil, nil, ErrNoSig
	}
	// Rcode, see msg.go Unpack()
	if int(dh.Bits&0xF) == RcodeNotAuth {
		return nil, nil, ErrAuth
	}

	// Arrays.
	dns.Question = make([]Question, dh.Qdcount)
	dns.Answer = make([]RR, dh.Ancount)
	dns.Ns = make([]RR, dh.Nscount)
	dns.Extra = make([]RR, dh.Arcount)

	for i := 0; i < len(dns.Question); i++ {
		off, ok = unpackStruct(&dns.Question[i], msg, off)
	}
	for i := 0; i < len(dns.Answer); i++ {
		dns.Answer[i], off, ok = unpackRR(msg, off)
	}
	for i := 0; i < len(dns.Ns); i++ {
		dns.Ns[i], off, ok = unpackRR(msg, off)
	}
	for i := 0; i < len(dns.Extra); i++ {
		tsigoff = off
		dns.Extra[i], off, ok = unpackRR(msg, off)
		if dns.Extra[i].Header().Rrtype == TypeTSIG {
			rr = dns.Extra[i].(*RR_TSIG)
			// Adjust Arcount.
			arcount, _ := unpackUint16(msg, 10)
			msg[10], msg[11] = packUint16(arcount - 1)
			break
		}
	}
	if !ok {
		return nil, nil, ErrUnpack
	}
	if rr == nil {
		return nil, nil, ErrNoSig
	}
	return msg[:tsigoff], rr, nil
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

	fs "gopkg.in/fsnotify.v1"
)

//
// Event
//
type Event struct {
	E fs.Event
	T time.Time
	I os.FileInfo
}

//
// chans
//
type ChanEvent chan *Event
type ChanExit chan bool

//
// Piper
//
type Piper interface {
	Pipe(next Piper) Piper
	Handle(e *Event)
	Close() error
}

//
// Watcher watch file changes
//
type Watcher struct {
	w    *fs.Watcher
	next Piper
	exit ChanExit
}

// NewWatcher create watcher
func NewWatcher(dirs []string) (*Watcher, error) {
	// w
	w, err := fs.NewWatcher()
	if err != nil {
		return nil, err
	}

	// listen
	log.Println("[umon]", "Watcher: watch dirs", dirs)
	for _, dir := range dirs {
		filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
			if info == nil {
				return err
			}
			if !info.IsDir() {
				return nil
			}
			w.Add(path)
			return nil
		})
	}

	// ok
	return &Watcher{
		w:    w,
		exit: make(ChanExit, 1),
	}, nil
}

// @impl Piper
func (w *Watcher) Pipe(next Piper) Piper {
	w.next = next
	go w.runWatch()
	return next
}

// runWatch run watch
func (w *Watcher) runWatch() {
	for {
		select {
		case <-w.exit:
			return

		case e := <-w.w.Events:
			info, _ := os.Stat(e.Name)
			if info != nil && info.IsDir() {
				switch e.Op {
				case fs.Create:
					w.w.Add(e.Name)
				case fs.Remove:
					w.w.Remove(e.Name)
				}
			}
			w.next.Handle(&Event{
				E: e,
				T: time.Now(),
				I: info,
			})
		}
	}
}

// @impl Piper.Handle
func (w *Watcher) Handle(e *Event) {
	// empty
}

// Close watch
func (w *Watcher) Close() error {
	w.exit <- true
	return nil
}

//
// Filter filter unwanted event
//
type Filter struct {
	next Piper

	events ChanEvent
	exit   ChanExit
}

func NewFilter() (*Filter, error) {
	return &Filter{
		exit:   make(ChanExit, 1),
		events: make(ChanEvent, 16),
	}, nil
}

// @impl Piper.Handle
func (f *Filter) Handle(e *Event) {
	f.events <- e
}

// @impl Piper.Pipe
func (f *Filter) Pipe(next Piper) Piper {
	f.next = next
	go f.runFilt()
	return next
}

// runFilter run filter
func (f *Filter) runFilt() {
	// last time when receive event
	var (
		lastTime time.Time
		lastEvt  *Event
	)

	// check event ticker
	ticker := time.NewTicker(time.Second)

	for {
		select {
		case <-f.exit:
			return

		case e := <-f.events:
			name := filepath.Base(e.E.Name)
			if strings.HasPrefix(name, ".") {
				continue
			}
			ext := filepath.Ext(name)
			if ext != ".go" {
				continue
			}
			lastTime = time.Now()
			lastEvt = e

		case <-ticker.C:
			if lastEvt == nil {
				continue
			}
			if time.Now().Sub(lastTime) < time.Second {
				continue
			}
			f.next.Handle(lastEvt)
			lastEvt = nil
		}
	}
}

// @impl Close
func (f *Filter) Close() error {
	f.exit <- true
	return nil
}

//
// Builder builder unwanted event
//
type Builder struct {
	next   Piper
	events ChanEvent
	exit   ChanExit
}

func NewBuilder() (*Builder, error) {
	return &Builder{
		exit:   make(ChanExit, 1),
		events: make(ChanEvent, 16),
	}, nil
}

// @impl Piper.Handle
func (b *Builder) Handle(e *Event) {
	b.events <- e
}

// @impl Piper.Pipe
func (b *Builder) Pipe(next Piper) Piper {
	b.next = next
	go b.runBuild()
	return next
}

// runBuild run builder
func (b *Builder) runBuild() {
	var (
		lastTime time.Time
		lastEvt  *Event
	)
	ticker := time.NewTicker(time.Second)

	for {
		select {
		case <-b.exit:
			return

		case e := <-b.events:
			cmd := exec.Command("go", "build")
			cmd.Stderr, cmd.Stdout = os.Stderr, os.Stdout
			if err := cmd.Start(); err != nil {
				log.Fatal(err)
			}
			if err := cmd.Wait(); err != nil {
				log.Println("[umon]", "Builder: build err", err)
			} else {
				lastTime = time.Now()
				lastEvt = e
				log.Println("[umon]", "Builder: build ok")
			}

		case <-ticker.C:
			if lastEvt == nil {
				continue
			}
			if time.Now().Sub(lastTime) < time.Second {
				continue
			}
			b.next.Handle(lastEvt)
			lastEvt = nil
		}
	}
}

// @impl Close
func (b *Builder) Close() error {
	b.exit <- true
	return nil
}

//
// Runner runner unwanted event
//
type Runner struct {
	next   Piper
	events ChanEvent
	exit   ChanExit
}

func NewRunner() (*Runner, error) {
	return &Runner{
		exit:   make(ChanExit, 1),
		events: make(ChanEvent, 16),
	}, nil
}

// @impl Piper.Handle
func (b *Runner) Handle(e *Event) {
	b.events <- e
}

// @impl Piper.Pipe
func (b *Runner) Pipe(next Piper) Piper {
	b.next = next
	go b.runRun()
	return next
}

// runRun run runner
func (b *Runner) runRun() {
	// app name
	runDir := os.Getenv("PWD")
	if len(runDir) == 0 {
		panic("no PWD env")
	}
	appName := filepath.Join(runDir, filepath.Base(runDir))
	log.Println("[umon]", "Runner: app", appName)

	// notify run
	runC := make(chan bool, 1)
	runC <- true

	// cmd
	var cmd *exec.Cmd
	for {
		select {
		case <-b.exit:
			return

		case <-b.events:
			runC <- true

		case <-runC:
			// kill
			if cmd != nil {
				log.Println("[umon]", "Runner: kill", cmd.Process)
				if cmd.Process != nil {
					cmd.Process.Kill()
				}
				cmd = nil
				time.Sleep(200*time.Microsecond)
			}

			// exist
			if _, err := os.Stat(appName); err != nil {
				if os.IsNotExist(err) {
					log.Println("[umon]", "Runner: app not exist")
				}
				continue
			}

			// start
			cmd = exec.Command(appName)
			cmd.Stderr, cmd.Stdout = os.Stderr, os.Stdout
			if err := cmd.Start(); err != nil {
				log.Println("[umon]", "Runner: start err", err)
			} else {
				log.Println("[umon]", "Runner: start ok")
			}
		}
	}
}

// @impl Close
func (b *Runner) Close() error {
	b.exit <- true
	return nil
}

//
// Ender
//
type Ender struct {
	// empty
}

// NewEnder create ender instance
func NewEnder() (*Ender, error) {
	return &Ender{
	// empty
	}, nil
}

// @impl Piper.Pipe
func (ed *Ender) Pipe(next Piper) Piper {
	// no next
	return nil
}

// @impl Piper.Handle
func (ed *Ender) Handle(e *Event) {
	log.Println("[umon]", "Ender: handle", e)
}

// @impl Close
func (ed *Ender) Close() error {
	return nil
}

//
// umon ../app ../ctrl
//
func main() {
	// watcher
	watchDir := []string{"."}
	if len(os.Args) > 1 {
		watchDir = os.Args[1:]
	}
	watch, err := NewWatcher(watchDir)
	if err != nil {
		log.Fatal(err)
	}
	defer watch.Close()

	// filter
	filter, err := NewFilter()
	if err != nil {
		log.Fatal(err)
	}
	defer filter.Close()

	// builder
	builder, err := NewBuilder()
	if err != nil {
		log.Fatal(err)
	}
	defer builder.Close()

	// runner
	runner, err := NewRunner()
	if err != nil {
		log.Fatal(err)
	}
	defer runner.Close()

	// end
	end, err := NewEnder()
	if err != nil {
		log.Fatal(err)
	}
	defer end.Close()

	// pipe
	watch.Pipe(filter).Pipe(builder).Pipe(runner).Pipe(end)

	// wait
	exit := make(ChanExit)
	<-exit
}
<commit_msg>add build hint<commit_after>package main

import (
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

	fs "gopkg.in/fsnotify.v1"
)

//
// Event
//
type Event struct {
	E fs.Event
	T time.Time
	I os.FileInfo
}

//
// chans
//
type ChanEvent chan *Event
type ChanExit chan bool

//
// Piper
//
type Piper interface {
	Pipe(next Piper) Piper
	Handle(e *Event)
	Close() error
}

//
// Watcher watch file changes
//
type Watcher struct {
	w    *fs.Watcher
	next Piper
	exit ChanExit
}

// NewWatcher create watcher
func NewWatcher(dirs []string) (*Watcher, error) {
	// w
	w, err := fs.NewWatcher()
	if err != nil {
		return nil, err
	}

	// listen
	log.Println("[umon]", "Watcher: watch dirs", dirs)
	for _, dir := range dirs {
		filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
			if info == nil {
				return err
			}
			if !info.IsDir() {
				return nil
			}
			w.Add(path)
			return nil
		})
	}

	// ok
	return &Watcher{
		w:    w,
		exit: make(ChanExit, 1),
	}, nil
}

// @impl Piper
func (w *Watcher) Pipe(next Piper) Piper {
	w.next = next
	go w.runWatch()
	return next
}

// runWatch run watch
func (w *Watcher) runWatch() {
	for {
		select {
		case <-w.exit:
			return

		case e := <-w.w.Events:
			info, _ := os.Stat(e.Name)
			if info != nil && info.IsDir() {
				switch e.Op {
				case fs.Create:
					w.w.Add(e.Name)
				case fs.Remove:
					w.w.Remove(e.Name)
				}
			}
			w.next.Handle(&Event{
				E: e,
				T: time.Now(),
				I: info,
			})
		}
	}
}

// @impl Piper.Handle
func (w *Watcher) Handle(e *Event) {
	// empty
}

// Close watch
func (w *Watcher) Close() error {
	w.exit <- true
	return nil
}

//
// Filter filter unwanted event
//
type Filter struct {
	next Piper

	events ChanEvent
	exit   ChanExit
}

func NewFilter() (*Filter, error) {
	return &Filter{
		exit:   make(ChanExit, 1),
		events: make(ChanEvent, 16),
	}, nil
}

// @impl Piper.Handle
func (f *Filter) Handle(e *Event) {
	f.events <- e
}

// @impl Piper.Pipe
func (f *Filter) Pipe(next Piper) Piper {
	f.next = next
	go f.runFilt()
	return next
}

// runFilter run filter
func (f *Filter) runFilt() {
	// last time when receive event
	var (
		lastTime time.Time
		lastEvt  *Event
	)

	// check event ticker
	ticker := time.NewTicker(time.Second)

	for {
		select {
		case <-f.exit:
			return

		case e := <-f.events:
			name := filepath.Base(e.E.Name)
			if strings.HasPrefix(name, ".") {
				continue
			}
			ext := filepath.Ext(name)
			if ext != ".go" {
				continue
			}
			lastTime = time.Now()
			lastEvt = e

		case <-ticker.C:
			if lastEvt == nil {
				continue
			}
			if time.Now().Sub(lastTime) < time.Second {
				continue
			}
			f.next.Handle(lastEvt)
			lastEvt = nil
		}
	}
}

// @impl Close
func (f *Filter) Close() error {
	f.exit <- true
	return nil
}

//
// Builder builder unwanted event
//
type Builder struct {
	next   Piper
	events ChanEvent
	exit   ChanExit
}

func NewBuilder() (*Builder, error) {
	return &Builder{
		exit:   make(ChanExit, 1),
		events: make(ChanEvent, 16),
	}, nil
}

// @impl Piper.Handle
func (b *Builder) Handle(e *Event) {
	b.events <- e
}

// @impl Piper.Pipe
func (b *Builder) Pipe(next Piper) Piper {
	b.next = next
	go b.runBuild()
	return next
}

// runBuild run builder
func (b *Builder) runBuild() {
	var (
		lastTime time.Time
		lastEvt  *Event
	)
	ticker := time.NewTicker(time.Second)

	for {
		select {
		case <-b.exit:
			return

		case e := <-b.events:
			cmd := exec.Command("go", "build")
			cmd.Stderr, cmd.Stdout = os.Stderr, os.Stdout
			if err := cmd.Start(); err != nil {
				log.Fatal(err)
			}
			if err := cmd.Wait(); err != nil {
				log.Println("[umon]", "Builder: build err", err)
			} else {
				lastTime = time.Now()
				lastEvt = e
				log.Println("[umon]", "Builder: build ok")
			}

		case <-ticker.C:
			if lastEvt == nil {
				continue
			}
			if time.Now().Sub(lastTime) < time.Second {
				continue
			}
			b.next.Handle(lastEvt)
			lastEvt = nil
		}
	}
}

// @impl Close
func (b *Builder) Close() error {
	b.exit <- true
	return nil
}

//
// Runner runner unwanted event
//
type Runner struct {
	next   Piper
	events ChanEvent
	exit   ChanExit
}

func NewRunner() (*Runner, error) {
	return &Runner{
		exit:   make(ChanExit, 1),
		events: make(ChanEvent, 16),
	}, nil
}

// @impl Piper.Handle
func (b *Runner) Handle(e *Event) {
	b.events <- e
}

// @impl Piper.Pipe
func (b *Runner) Pipe(next Piper) Piper {
	b.next = next
	go b.runRun()
	return next
}

// runRun run runner
func (b *Runner) runRun() {
	// app name
	runDir := os.Getenv("PWD")
	if len(runDir) == 0 {
		panic("no PWD env")
	}
	appName := filepath.Join(runDir, filepath.Base(runDir))
	log.Println("[umon]", "Runner: app", appName)

	// notify run
	runC := make(chan bool, 1)
	runC <- true

	// cmd
	var cmd *exec.Cmd
	for {
		select {
		case <-b.exit:
			return

		case <-b.events:
			runC <- true

		case <-runC:
			// kill
			if cmd != nil {
				log.Println("[umon]", "Runner: kill", cmd.Process)
				if cmd.Process != nil {
					cmd.Process.Kill()
				}
				cmd = nil
				time.Sleep(200*time.Microsecond)
			}

			// exist
			if _, err := os.Stat(appName); err != nil {
				if os.IsNotExist(err) {
					log.Println("[umon]", "Runner: app not exist, run go build first")
				}
				continue
			}

			// start
			cmd = exec.Command(appName)
			cmd.Stderr, cmd.Stdout = os.Stderr, os.Stdout
			if err := cmd.Start(); err != nil {
				log.Println("[umon]", "Runner: start err", err)
			} else {
				log.Println("[umon]", "Runner: start ok")
			}
		}
	}
}

// @impl Close
func (b *Runner) Close() error {
	b.exit <- true
	return nil
}

//
// Ender
//
type Ender struct {
	// empty
}

// NewEnder create ender instance
func NewEnder() (*Ender, error) {
	return &Ender{
	// empty
	}, nil
}

// @impl Piper.Pipe
func (ed *Ender) Pipe(next Piper) Piper {
	// no next
	return nil
}

// @impl Piper.Handle
func (ed *Ender) Handle(e *Event) {
	log.Println("[umon]", "Ender: handle", e)
}

// @impl Close
func (ed *Ender) Close() error {
	return nil
}

//
// umon ../app ../ctrl
//
func main() {
	// watcher
	watchDir := []string{"."}
	if len(os.Args) > 1 {
		watchDir = os.Args[1:]
	}
	watch, err := NewWatcher(watchDir)
	if err != nil {
		log.Fatal(err)
	}
	defer watch.Close()

	// filter
	filter, err := NewFilter()
	if err != nil {
		log.Fatal(err)
	}
	defer filter.Close()

	// builder
	builder, err := NewBuilder()
	if err != nil {
		log.Fatal(err)
	}
	defer builder.Close()

	// runner
	runner, err := NewRunner()
	if err != nil {
		log.Fatal(err)
	}
	defer runner.Close()

	// end
	end, err := NewEnder()
	if err != nil {
		log.Fatal(err)
	}
	defer end.Close()

	// pipe
	watch.Pipe(filter).Pipe(builder).Pipe(runner).Pipe(end)

	// wait
	exit := make(ChanExit)
	<-exit
}
<|endoftext|>


<commit_before>package spotify

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

// User contains the basic, publicly available information about a Spotify user.
type User struct {
	// The name displayed on the user's profile.
	// Note: Spotify currently fails to populate
	// this field when querying for a playlist.
	DisplayName string `json:"display_name"`
	// Known public external URLs for the user.
	ExternalURLs map[string]string `json:"external_urls"`
	// Information about followers of the user.
	Followers Followers `json:"followers"`
	// A link to the Web API endpoint for this user.
	Endpoint string `json:"href"`
	// The Spotify user ID for the user.
	ID string `json:"id"`
	// The user's profile image.
	Images []Image `json:"images"`
	// The Spotify URI for the user.
	URI URI `json:"uri"`
}

// PrivateUser contains additional information about a user.
// This data is private and requires user authentication.
type PrivateUser struct {
	User
	// The country of the user, as set in the user's account profile.
	// An ISO 3166-1 alpha-2 country code.  This field is only available when the
	// current user has granted acess to the ScopeUserReadPrivate scope.
	Country string `json:"country"`
	// The user's email address, as entered by the user when creating their account.
	// Note: this email is UNVERIFIED - there is no proof that it actually
	// belongs to the user.  This field is only available when the current user
	// has granted access to the ScopeUserReadEmail scope.
	Email string `json:"email"`
	// The user's Spotify subscription level: "premium", "free", etc.
	// The subscription level "open" can be considered the same as "free".
	// This field is only available when the current user has granted access to
	// the ScopeUserReadPrivate scope.
	Product string `json:"product"`
	// The user's date of birth, in the format 'YYYY-MM-DD'.  You can use
	// the DateLayout constant to convert this to a time.Time value.
	// This field is only available when the current user has granted
	// access to the ScopeUserReadBirthdate scope.
	Birthdate string `json:"birthdate"`
}

// GetUsersPublicProfile gets public profile information about a
// Spotify User.  It does not require authentication.
func (c *Client) GetUsersPublicProfile(userID ID) (*User, error) {
	spotifyURL := c.baseURL + "users/" + string(userID)

	var user User

	err := c.get(spotifyURL, &user)
	if err != nil {
		return nil, err
	}

	return &user, nil
}

// CurrentUser gets detailed profile information about the
// current user.
//
// Reading the user's email address requires that the application
// has the ScopeUserReadEmail scope.  Reading the country, display
// name, profile images, and product subscription level requires
// that the application has the ScopeUserReadPrivate scope.
//
// Warning: The email address in the response will be the address
// that was entered when the user created their spotify account.
// This email address is unverified - do not assume that Spotify has
// checked that the email address actually belongs to the user.
func (c *Client) CurrentUser() (*PrivateUser, error) {
	var result PrivateUser

	err := c.get(c.baseURL+"me", &result)
	if err != nil {
		return nil, err
	}

	return &result, nil
}

// CurrentUsersTracks gets a list of songs saved in the current
// Spotify user's "Your Music" library.
func (c *Client) CurrentUsersTracks() (*SavedTrackPage, error) {
	return c.CurrentUsersTracksOpt(nil)
}

// CurrentUsersTracksOpt is like CurrentUsersTracks, but it accepts additional
// options for sorting and filtering the results.
func (c *Client) CurrentUsersTracksOpt(opt *Options) (*SavedTrackPage, error) {
	spotifyURL := c.baseURL + "me/tracks"
	if opt != nil {
		v := url.Values{}
		if opt.Country != nil {
			v.Set("country", *opt.Country)
		}
		if opt.Limit != nil {
			v.Set("limit", strconv.Itoa(*opt.Limit))
		}
		if opt.Offset != nil {
			v.Set("offset", strconv.Itoa(*opt.Offset))
		}
		if params := v.Encode(); params != "" {
			spotifyURL += "?" + params
		}
	}

	var result SavedTrackPage

	err := c.get(spotifyURL, &result)
	if err != nil {
		return nil, err
	}

	return &result, nil
}

// FollowUser adds the current user as a follower of one or more
// spotify users, identified by their Spotify IDs.
//
// Modifying the lists of artists or users the current user follows
// requires that the application has the ScopeUserFollowModify scope.
func (c *Client) FollowUser(ids ...ID) error {
	return c.modifyFollowers("user", true, ids...)
}

// FollowArtist adds the current user as a follower of one or more
// spotify artists, identified by their Spotify IDs.
//
// Modifying the lists of artists or users the current user follows
// requires that the application has the ScopeUserFollowModify scope.
func (c *Client) FollowArtist(ids ...ID) error {
	return c.modifyFollowers("artist", true, ids...)
}

// UnfollowUser removes the current user as a follower of one or more
// Spotify users.
//
// Modifying the lists of artists or users the current user follows
// requires that the application has the ScopeUserFollowModify scope.
func (c *Client) UnfollowUser(ids ...ID) error {
	return c.modifyFollowers("user", false, ids...)
}

// UnfollowArtist removes the current user as a follower of one or more
// Spotify artists.
//
// Modifying the lists of artists or users the current user follows
// requires that the application has the ScopeUserFollowModify scope.
func (c *Client) UnfollowArtist(ids ...ID) error {
	return c.modifyFollowers("artist", false, ids...)
}

// CurrentUserFollows checks to see if the current user is following
// one or more artists or other Spotify Users.  This call requires
// ScopeUserFollowRead.
//
// The t argument indicates the type of the IDs, and must be either
// "user" or "artist".
//
// The result is returned as a slice of bool values in the same order
// in which the IDs were specified.
func (c *Client) CurrentUserFollows(t string, ids ...ID) ([]bool, error) {
	if l := len(ids); l == 0 || l > 50 {
		return nil, errors.New("spotify: UserFollows supports 1 to 50 IDs")
	}
	if t != "artist" && t != "user" {
		return nil, errors.New("spotify: t must be 'artist' or 'user'")
	}
	spotifyURL := fmt.Sprintf("%sme/following/contains?type=%s&ids=%s",
		c.baseURL, t, strings.Join(toStringSlice(ids), ","))

	var result []bool

	err := c.get(spotifyURL, &result)
	if err != nil {
		return nil, err
	}

	return result, nil
}

func (c *Client) modifyFollowers(usertype string, follow bool, ids ...ID) error {
	if l := len(ids); l == 0 || l > 50 {
		return errors.New("spotify: Follow/Unfollow supports 1 to 50 IDs")
	}
	v := url.Values{}
	v.Add("type", usertype)
	v.Add("ids", strings.Join(toStringSlice(ids), ","))
	spotifyURL := c.baseURL + "me/following?" + v.Encode()
	method := "PUT"
	if !follow {
		method = "DELETE"
	}
	req, err := http.NewRequest(method, spotifyURL, nil)
	if err != nil {
		return err
	}
	err = c.execute(req, nil, http.StatusNoContent)
	if err != nil {
		return err
	}
	return nil
}

// CurrentUsersFollowedArtists gets the current user's followed artists.
// This call requires that the user has granted the ScopeUserFollowRead scope.
func (c *Client) CurrentUsersFollowedArtists() (*FullArtistCursorPage, error) {
	return c.CurrentUsersFollowedArtistsOpt(-1, "")
}

// CurrentUsersFollowedArtistsOpt is like CurrentUsersFollowedArtists,
// but it accept the optional arguments limit and after.  Limit is the
// maximum number of items to return (1 <= limit <= 50), and after is
// the last artist ID retrieved from the previous request.  If you don't
// wish to specify either of the parameters, use -1 for limit and the empty
// string for after.
func (c *Client) CurrentUsersFollowedArtistsOpt(limit int, after string) (*FullArtistCursorPage, error) {
	spotifyURL := c.baseURL + "me/following"
	v := url.Values{}
	v.Set("type", "artist")
	if limit != -1 {
		v.Set("limit", strconv.Itoa(limit))
	}
	if after != "" {
		v.Set("after", after)
	}
	if params := v.Encode(); params != "" {
		spotifyURL += "?" + params
	}

	var result struct {
		A FullArtistCursorPage `json:"artists"`
	}

	err := c.get(spotifyURL, &result)
	if err != nil {
		return nil, err
	}

	return &result.A, nil
}

// CurrentUsersAlbums gets a list of albums saved in the current
// Spotify user's "Your Music" library.
func (c *Client) CurrentUsersAlbums() (*SavedAlbumPage, error) {
	return c.CurrentUsersAlbumsOpt(nil)
}

// CurrentUsersAlbumsOpt is like CurrentUsersAlbums, but it accepts additional
// options for sorting and filtering the results.
func (c *Client) CurrentUsersAlbumsOpt(opt *Options) (*SavedAlbumPage, error) {
	spotifyURL := c.baseURL + "me/albums"
	if opt != nil {
		v := url.Values{}
		if opt.Country != nil {
			v.Set("market", *opt.Country)
		}
		if opt.Limit != nil {
			v.Set("limit", strconv.Itoa(*opt.Limit))
		}
		if opt.Offset != nil {
			v.Set("offset", strconv.Itoa(*opt.Offset))
		}
		if params := v.Encode(); params != "" {
			spotifyURL += "?" + params
		}
	}

	var result SavedAlbumPage

	err := c.get(spotifyURL, &result)
	if err != nil {
		return nil, err
	}

	return &result, nil
}

// CurrentUsersPlaylists gets a list of the playlists owned or followed by
// the current spotify user.
//
// Private playlists require the ScopePlaylistReadPrivate scope.  Note that
// this scope alone will not return collaborative playlists, even though
// they are always private.  In order to retrieve collaborative playlists
// the user must authorize the ScopePlaylistReadCollaborative scope.
func (c *Client) CurrentUsersPlaylists() (*SimplePlaylistPage, error) {
	return c.CurrentUsersPlaylistsOpt(nil)
}

// CurrentUsersPlaylistsOpt is like CurrentUsersPlaylists, but it accepts
// additional options for sorting and filtering the results.
func (c *Client) CurrentUsersPlaylistsOpt(opt *Options) (*SimplePlaylistPage, error) {
	spotifyURL := c.baseURL + "me/playlists"
	if opt != nil {
		v := url.Values{}
		if opt.Limit != nil {
			v.Set("limit", strconv.Itoa(*opt.Limit))
		}
		if opt.Offset != nil {
			v.Set("offset", strconv.Itoa(*opt.Offset))
		}
		if params := v.Encode(); params != "" {
			spotifyURL += "?" + params
		}
	}

	var result SimplePlaylistPage

	err := c.get(spotifyURL, &result)
	if err != nil {
		return nil, err
	}

	return &result, nil
}

// CurrentUsersTopArtistsOpt gets a list of the top played artists in a given time
// range of the current Spotify user. It supports up to 50 artists in a single
// call. This call requires ScopeUserTopRead.
func (c *Client) CurrentUsersTopArtistsOpt(opt *Options) (*FullArtistPage, error) {
	spotifyURL := c.baseURL + "me/top/artists"
	if opt != nil {
		v := url.Values{}
		if opt.Limit != nil {
			v.Set("limit", strconv.Itoa(*opt.Limit))
		}
		if opt.Timerange != nil {
			v.Set("time_range", *opt.Timerange+"_term")
		}
		if params := v.Encode(); params != "" {
			spotifyURL += "?" + params
		}
	}

	var result FullArtistPage

	err := c.get(spotifyURL, &result)
	if err != nil {
		return nil, err
	}

	return &result, nil
}

// CurrentUsersTopArtists is like CurrentUsersTopArtistsOpt but with
// sensible defaults. The default limit is 20 and the default timerange
// is medium_term.
func (c *Client) CurrentUsersTopArtists() (*FullArtistPage, error) {
	return c.CurrentUsersTopArtistsOpt(nil)
}

// CurrentUsersTopTracksOpt gets a list of the top played tracks in a given time
// range of the current Spotify user. It supports up to 50 tracks in a single
// call. This call requires ScopeUserTopRead.
func (c *Client) CurrentUsersTopTracksOpt(opt *Options) (*FullTrackPage, error) {
	spotifyURL := c.baseURL + "me/top/tracks"
	if opt != nil {
		v := url.Values{}
		if opt.Limit != nil {
			v.Set("limit", strconv.Itoa(*opt.Limit))
		}
		if opt.Timerange != nil {
			v.Set("time_range", *opt.Timerange+"_term")
		}
		if params := v.Encode(); params != "" {
			spotifyURL += "?" + params
		}
	}

	var result FullTrackPage

	err := c.get(spotifyURL, &result)
	if err != nil {
		return nil, err
	}

	return &result, nil
}

// CurrentUsersTopTracks is like CurrentUsersTopTracksOpt but with
// sensible defaults. The default limit is 20 and the default timerange
// is medium_term.
func (c *Client) CurrentUsersTopTracks() (*FullTrackPage, error) {
	return c.CurrentUsersTopTracksOpt(nil)
}<commit_msg>Added offset to toptracks (#66)<commit_after>package spotify

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

// User contains the basic, publicly available information about a Spotify user.
type User struct {
	// The name displayed on the user's profile.
	// Note: Spotify currently fails to populate
	// this field when querying for a playlist.
	DisplayName string `json:"display_name"`
	// Known public external URLs for the user.
	ExternalURLs map[string]string `json:"external_urls"`
	// Information about followers of the user.
	Followers Followers `json:"followers"`
	// A link to the Web API endpoint for this user.
	Endpoint string `json:"href"`
	// The Spotify user ID for the user.
	ID string `json:"id"`
	// The user's profile image.
	Images []Image `json:"images"`
	// The Spotify URI for the user.
	URI URI `json:"uri"`
}

// PrivateUser contains additional information about a user.
// This data is private and requires user authentication.
type PrivateUser struct {
	User
	// The country of the user, as set in the user's account profile.
	// An ISO 3166-1 alpha-2 country code.  This field is only available when the
	// current user has granted acess to the ScopeUserReadPrivate scope.
	Country string `json:"country"`
	// The user's email address, as entered by the user when creating their account.
	// Note: this email is UNVERIFIED - there is no proof that it actually
	// belongs to the user.  This field is only available when the current user
	// has granted access to the ScopeUserReadEmail scope.
	Email string `json:"email"`
	// The user's Spotify subscription level: "premium", "free", etc.
	// The subscription level "open" can be considered the same as "free".
	// This field is only available when the current user has granted access to
	// the ScopeUserReadPrivate scope.
	Product string `json:"product"`
	// The user's date of birth, in the format 'YYYY-MM-DD'.  You can use
	// the DateLayout constant to convert this to a time.Time value.
	// This field is only available when the current user has granted
	// access to the ScopeUserReadBirthdate scope.
	Birthdate string `json:"birthdate"`
}

// GetUsersPublicProfile gets public profile information about a
// Spotify User.  It does not require authentication.
func (c *Client) GetUsersPublicProfile(userID ID) (*User, error) {
	spotifyURL := c.baseURL + "users/" + string(userID)

	var user User

	err := c.get(spotifyURL, &user)
	if err != nil {
		return nil, err
	}

	return &user, nil
}

// CurrentUser gets detailed profile information about the
// current user.
//
// Reading the user's email address requires that the application
// has the ScopeUserReadEmail scope.  Reading the country, display
// name, profile images, and product subscription level requires
// that the application has the ScopeUserReadPrivate scope.
//
// Warning: The email address in the response will be the address
// that was entered when the user created their spotify account.
// This email address is unverified - do not assume that Spotify has
// checked that the email address actually belongs to the user.
func (c *Client) CurrentUser() (*PrivateUser, error) {
	var result PrivateUser

	err := c.get(c.baseURL+"me", &result)
	if err != nil {
		return nil, err
	}

	return &result, nil
}

// CurrentUsersTracks gets a list of songs saved in the current
// Spotify user's "Your Music" library.
func (c *Client) CurrentUsersTracks() (*SavedTrackPage, error) {
	return c.CurrentUsersTracksOpt(nil)
}

// CurrentUsersTracksOpt is like CurrentUsersTracks, but it accepts additional
// options for sorting and filtering the results.
func (c *Client) CurrentUsersTracksOpt(opt *Options) (*SavedTrackPage, error) {
	spotifyURL := c.baseURL + "me/tracks"
	if opt != nil {
		v := url.Values{}
		if opt.Country != nil {
			v.Set("country", *opt.Country)
		}
		if opt.Limit != nil {
			v.Set("limit", strconv.Itoa(*opt.Limit))
		}
		if opt.Offset != nil {
			v.Set("offset", strconv.Itoa(*opt.Offset))
		}
		if params := v.Encode(); params != "" {
			spotifyURL += "?" + params
		}
	}

	var result SavedTrackPage

	err := c.get(spotifyURL, &result)
	if err != nil {
		return nil, err
	}

	return &result, nil
}

// FollowUser adds the current user as a follower of one or more
// spotify users, identified by their Spotify IDs.
//
// Modifying the lists of artists or users the current user follows
// requires that the application has the ScopeUserFollowModify scope.
func (c *Client) FollowUser(ids ...ID) error {
	return c.modifyFollowers("user", true, ids...)
}

// FollowArtist adds the current user as a follower of one or more
// spotify artists, identified by their Spotify IDs.
//
// Modifying the lists of artists or users the current user follows
// requires that the application has the ScopeUserFollowModify scope.
func (c *Client) FollowArtist(ids ...ID) error {
	return c.modifyFollowers("artist", true, ids...)
}

// UnfollowUser removes the current user as a follower of one or more
// Spotify users.
//
// Modifying the lists of artists or users the current user follows
// requires that the application has the ScopeUserFollowModify scope.
func (c *Client) UnfollowUser(ids ...ID) error {
	return c.modifyFollowers("user", false, ids...)
}

// UnfollowArtist removes the current user as a follower of one or more
// Spotify artists.
//
// Modifying the lists of artists or users the current user follows
// requires that the application has the ScopeUserFollowModify scope.
func (c *Client) UnfollowArtist(ids ...ID) error {
	return c.modifyFollowers("artist", false, ids...)
}

// CurrentUserFollows checks to see if the current user is following
// one or more artists or other Spotify Users.  This call requires
// ScopeUserFollowRead.
//
// The t argument indicates the type of the IDs, and must be either
// "user" or "artist".
//
// The result is returned as a slice of bool values in the same order
// in which the IDs were specified.
func (c *Client) CurrentUserFollows(t string, ids ...ID) ([]bool, error) {
	if l := len(ids); l == 0 || l > 50 {
		return nil, errors.New("spotify: UserFollows supports 1 to 50 IDs")
	}
	if t != "artist" && t != "user" {
		return nil, errors.New("spotify: t must be 'artist' or 'user'")
	}
	spotifyURL := fmt.Sprintf("%sme/following/contains?type=%s&ids=%s",
		c.baseURL, t, strings.Join(toStringSlice(ids), ","))

	var result []bool

	err := c.get(spotifyURL, &result)
	if err != nil {
		return nil, err
	}

	return result, nil
}

func (c *Client) modifyFollowers(usertype string, follow bool, ids ...ID) error {
	if l := len(ids); l == 0 || l > 50 {
		return errors.New("spotify: Follow/Unfollow supports 1 to 50 IDs")
	}
	v := url.Values{}
	v.Add("type", usertype)
	v.Add("ids", strings.Join(toStringSlice(ids), ","))
	spotifyURL := c.baseURL + "me/following?" + v.Encode()
	method := "PUT"
	if !follow {
		method = "DELETE"
	}
	req, err := http.NewRequest(method, spotifyURL, nil)
	if err != nil {
		return err
	}
	err = c.execute(req, nil, http.StatusNoContent)
	if err != nil {
		return err
	}
	return nil
}

// CurrentUsersFollowedArtists gets the current user's followed artists.
// This call requires that the user has granted the ScopeUserFollowRead scope.
func (c *Client) CurrentUsersFollowedArtists() (*FullArtistCursorPage, error) {
	return c.CurrentUsersFollowedArtistsOpt(-1, "")
}

// CurrentUsersFollowedArtistsOpt is like CurrentUsersFollowedArtists,
// but it accept the optional arguments limit and after.  Limit is the
// maximum number of items to return (1 <= limit <= 50), and after is
// the last artist ID retrieved from the previous request.  If you don't
// wish to specify either of the parameters, use -1 for limit and the empty
// string for after.
func (c *Client) CurrentUsersFollowedArtistsOpt(limit int, after string) (*FullArtistCursorPage, error) {
	spotifyURL := c.baseURL + "me/following"
	v := url.Values{}
	v.Set("type", "artist")
	if limit != -1 {
		v.Set("limit", strconv.Itoa(limit))
	}
	if after != "" {
		v.Set("after", after)
	}
	if params := v.Encode(); params != "" {
		spotifyURL += "?" + params
	}

	var result struct {
		A FullArtistCursorPage `json:"artists"`
	}

	err := c.get(spotifyURL, &result)
	if err != nil {
		return nil, err
	}

	return &result.A, nil
}

// CurrentUsersAlbums gets a list of albums saved in the current
// Spotify user's "Your Music" library.
func (c *Client) CurrentUsersAlbums() (*SavedAlbumPage, error) {
	return c.CurrentUsersAlbumsOpt(nil)
}

// CurrentUsersAlbumsOpt is like CurrentUsersAlbums, but it accepts additional
// options for sorting and filtering the results.
func (c *Client) CurrentUsersAlbumsOpt(opt *Options) (*SavedAlbumPage, error) {
	spotifyURL := c.baseURL + "me/albums"
	if opt != nil {
		v := url.Values{}
		if opt.Country != nil {
			v.Set("market", *opt.Country)
		}
		if opt.Limit != nil {
			v.Set("limit", strconv.Itoa(*opt.Limit))
		}
		if opt.Offset != nil {
			v.Set("offset", strconv.Itoa(*opt.Offset))
		}
		if params := v.Encode(); params != "" {
			spotifyURL += "?" + params
		}
	}

	var result SavedAlbumPage

	err := c.get(spotifyURL, &result)
	if err != nil {
		return nil, err
	}

	return &result, nil
}

// CurrentUsersPlaylists gets a list of the playlists owned or followed by
// the current spotify user.
//
// Private playlists require the ScopePlaylistReadPrivate scope.  Note that
// this scope alone will not return collaborative playlists, even though
// they are always private.  In order to retrieve collaborative playlists
// the user must authorize the ScopePlaylistReadCollaborative scope.
func (c *Client) CurrentUsersPlaylists() (*SimplePlaylistPage, error) {
	return c.CurrentUsersPlaylistsOpt(nil)
}

// CurrentUsersPlaylistsOpt is like CurrentUsersPlaylists, but it accepts
// additional options for sorting and filtering the results.
func (c *Client) CurrentUsersPlaylistsOpt(opt *Options) (*SimplePlaylistPage, error) {
	spotifyURL := c.baseURL + "me/playlists"
	if opt != nil {
		v := url.Values{}
		if opt.Limit != nil {
			v.Set("limit", strconv.Itoa(*opt.Limit))
		}
		if opt.Offset != nil {
			v.Set("offset", strconv.Itoa(*opt.Offset))
		}
		if params := v.Encode(); params != "" {
			spotifyURL += "?" + params
		}
	}

	var result SimplePlaylistPage

	err := c.get(spotifyURL, &result)
	if err != nil {
		return nil, err
	}

	return &result, nil
}

// CurrentUsersTopArtistsOpt gets a list of the top played artists in a given time
// range of the current Spotify user. It supports up to 50 artists in a single
// call. This call requires ScopeUserTopRead.
func (c *Client) CurrentUsersTopArtistsOpt(opt *Options) (*FullArtistPage, error) {
	spotifyURL := c.baseURL + "me/top/artists"
	if opt != nil {
		v := url.Values{}
		if opt.Limit != nil {
			v.Set("limit", strconv.Itoa(*opt.Limit))
		}
		if opt.Timerange != nil {
			v.Set("time_range", *opt.Timerange+"_term")
		}
		if params := v.Encode(); params != "" {
			spotifyURL += "?" + params
		}
	}

	var result FullArtistPage

	err := c.get(spotifyURL, &result)
	if err != nil {
		return nil, err
	}

	return &result, nil
}

// CurrentUsersTopArtists is like CurrentUsersTopArtistsOpt but with
// sensible defaults. The default limit is 20 and the default timerange
// is medium_term.
func (c *Client) CurrentUsersTopArtists() (*FullArtistPage, error) {
	return c.CurrentUsersTopArtistsOpt(nil)
}

// CurrentUsersTopTracksOpt gets a list of the top played tracks in a given time
// range of the current Spotify user. It supports up to 50 tracks in a single
// call. This call requires ScopeUserTopRead.
func (c *Client) CurrentUsersTopTracksOpt(opt *Options) (*FullTrackPage, error) {
	spotifyURL := c.baseURL + "me/top/tracks"
	if opt != nil {
		v := url.Values{}
		if opt.Limit != nil {
			v.Set("limit", strconv.Itoa(*opt.Limit))
		}
		if opt.Timerange != nil {
			v.Set("time_range", *opt.Timerange+"_term")
		}
		if opt.Offset != nil {
			v.Set("offset", strconv.Itoa(*opt.Offset))
		}
		if params := v.Encode(); params != "" {
			spotifyURL += "?" + params
		}
	}

	var result FullTrackPage

	err := c.get(spotifyURL, &result)
	if err != nil {
		return nil, err
	}

	return &result, nil
}

// CurrentUsersTopTracks is like CurrentUsersTopTracksOpt but with
// sensible defaults. The default limit is 20 and the default timerange
// is medium_term.
func (c *Client) CurrentUsersTopTracks() (*FullTrackPage, error) {
	return c.CurrentUsersTopTracksOpt(nil)
}<|endoftext|>


<commit_before>package TeleGogo

// User represents a Telegram user or bot.
type User struct {
	// ID Unique identifier for this user or bot
	ID int `json:"id"`
	// FirstName User‘s or bot’s first name
	FirstName string `json:"first_name"`
	// LastName Optional. User‘s or bot’s last name
	LastName string `json:"last_name"`
	// Username Optional. User‘s or bot’s username
	Username string `json:"username"`
}

func userFromJSON() (User, error) {
	return User{}, nil
}

// UserProfilePhotos represent a user's profile pictures.
type UserProfilePhotos struct {
	// Count Total number of profile pictures the target user has
	Count int `json:"total_count"`
	// Photos Requested profile pictures (in up to 4 sizes each)
	Photos []PhotoSize `json:"photos"`
}

type userResponse struct {
	OK     bool `json:"ok"`
	Result User `json:"result"`
}
<commit_msg>fixed UserProfilePhotos having an array of PhotoSize instead of an Array of an Array<commit_after>package TeleGogo

// User represents a Telegram user or bot.
type User struct {
	// ID Unique identifier for this user or bot
	ID int `json:"id"`
	// FirstName User‘s or bot’s first name
	FirstName string `json:"first_name"`
	// LastName Optional. User‘s or bot’s last name
	LastName string `json:"last_name"`
	// Username Optional. User‘s or bot’s username
	Username string `json:"username"`
}

func userFromJSON() (User, error) {
	return User{}, nil
}

// UserProfilePhotos represent a user's profile pictures.
type UserProfilePhotos struct {
	// Count Total number of profile pictures the target user has
	Count int `json:"total_count"`
	// Photos Requested profile pictures (in up to 4 sizes each)
	Photos [][]PhotoSize `json:"photos"`
}

type userResponse struct {
	OK     bool `json:"ok"`
	Result User `json:"result"`
}
<|endoftext|>


<commit_before>package goinsta

// User is the representation of instagram's user profile
type User struct {
	//Feed *Feed
	//Followers *Followers
	//Following *Following
	//Status *Friendship
	//Story *Story
	//Messages *Messages

	ID                         int64        `json:"pk"`
	Username                   string       `json:"username"`
	FullName                   string       `json:"full_name"`
	HasAnonymousProfilePicture bool         `json:"has_anonymous_profile_picture"`
	IsPrivate                  bool         `json:"is_private"`
	IsVerified                 bool         `json:"is_verified"`
	ProfilePicURL              string       `json:"profile_pic_url"`
	ProfilePicID               string       `json:"profile_pic_id"`
	MediaCount                 int          `json:"media_count"`
	FollowerCount              int          `json:"follower_count"`
	FollowingCount             int          `json:"following_count"`
	GeoMediaCount              int          `json:"geo_media_count"`
	IsBusiness                 bool         `json:"is_business"`
	Biography                  string       `json:"biography"`
	ExternalURL                string       `json:"external_url"`
	HasBiographyTranslation    bool         `json:"has_biography_translation"`
	ExternalLynxURL            string       `json:"external_lynx_url"`
	HdProfilePicURLInfo        PicURLInfo   `json:"hd_profile_pic_url_info"`
	HdProfilePicVersions       []PicURLInfo `json:"hd_profile_pic_versions"`
	UsertagsCount              int          `json:"usertags_count"`
	HasChaining                bool         `json:"has_chaining"`
	IsFavorite                 bool         `json:"is_favorite"`
	ReelAutoArchive            string       `json:"reel_auto_archive"`
	School                     School       `json:"school"`
	PublicEmail                string       `json:"public_email"`
	PublicPhoneNumber          string       `json:"public_phone_number"`
	PublicPhoneCountryCode     string       `json:"public_phone_country_code"`
	ContactPhoneNumber         string       `json:"contact_phone_number"`
	CityID                     int64        `json:"city_id"`
	CityName                   string       `json:"city_name"`
	AddressStreet              string       `json:"address_street"`
	DirectMessaging            string       `json:"direct_messaging"`
	Latitude                   float64      `json:"latitude"`
	Longitude                  float64      `json:"longitude"`
	Category                   string       `json:"category"`
	BusinessContactMethod      string       `json:"business_contact_method"`
	IsCallToActionEnabled      bool         `json:"is_call_to_action_enabled"`
	FbPageCallToActionID       string       `json:"fb_page_call_to_action_id"`
	Zip                        string       `json:"zip"`
}
<commit_msg>Updated user.go values<commit_after>package goinsta

// User is the representation of instagram's user profile
type User struct {
	//Feed *Feed
	//Followers *Followers
	//Following *Following
	//Status *Friendship
	//Story *Story
	//Messages *Messages

	// User values shared between user and account
	ID            int64  `json:"pk"`
	Username      string `json:"username"`
	FullName      string `json:"full_name"`
	Biography     string `json:"biography"`
	ProfilePicURL string `json:"profile_pic_url"`
	Email         string `json:"email"`
	PhoneNumber   string `json:"phone_number"`
	IsBusiness    bool   `json:"is_business"`
	Gender        int    `json:"gender"`

	ProfilePicID               string       `json:"profile_pic_id"`
	HasAnonymousProfilePicture bool         `json:"has_anonymous_profile_picture"`
	IsPrivate                  bool         `json:"is_private"`
	IsVerified                 bool         `json:"is_verified"`
	MediaCount                 int          `json:"media_count"`
	FollowerCount              int          `json:"follower_count"`
	FollowingCount             int          `json:"following_count"`
	GeoMediaCount              int          `json:"geo_media_count"`
	ExternalURL                string       `json:"external_url"`
	HasBiographyTranslation    bool         `json:"has_biography_translation"`
	ExternalLynxURL            string       `json:"external_lynx_url"`
	HdProfilePicURLInfo        PicURLInfo   `json:"hd_profile_pic_url_info"`
	HdProfilePicVersions       []PicURLInfo `json:"hd_profile_pic_versions"`
	UsertagsCount              int          `json:"usertags_count"`
	HasChaining                bool         `json:"has_chaining"`
	IsFavorite                 bool         `json:"is_favorite"`
	ReelAutoArchive            string       `json:"reel_auto_archive"`
	School                     School       `json:"school"`
	PublicEmail                string       `json:"public_email"`
	PublicPhoneNumber          string       `json:"public_phone_number"`
	PublicPhoneCountryCode     string       `json:"public_phone_country_code"`
	ContactPhoneNumber         string       `json:"contact_phone_number"`
	CityID                     int64        `json:"city_id"`
	CityName                   string       `json:"city_name"`
	AddressStreet              string       `json:"address_street"`
	DirectMessaging            string       `json:"direct_messaging"`
	Latitude                   float64      `json:"latitude"`
	Longitude                  float64      `json:"longitude"`
	Category                   string       `json:"category"`
	BusinessContactMethod      string       `json:"business_contact_method"`
	IsCallToActionEnabled      bool         `json:"is_call_to_action_enabled"`
	FbPageCallToActionID       string       `json:"fb_page_call_to_action_id"`
	Zip                        string       `json:"zip"`
}
<|endoftext|>


<commit_before>package nucular

import (
	"image"
	"strings"

	nstyle "github.com/aarzilli/nucular/style"

	"golang.org/x/image/font"
	"golang.org/x/mobile/event/mouse"

	"github.com/aarzilli/nucular/rect"

	"github.com/hashicorp/golang-lru"
)

type Heading int

const (
	Up Heading = iota
	Right
	Down
	Left
)

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

func assert(cond bool) {
	if !cond {
		panic("assert!")
	}
}

func assert2(cond bool, reason string) {
	if !cond {
		panic(reason)
	}
}

func triangleFromDirection(r rect.Rect, pad_x, pad_y int, direction Heading) (result [3]image.Point) {
	var w_half int
	var h_half int

	r.W = max(2*pad_x, r.W)
	r.H = max(2*pad_y, r.H)
	r.W = r.W - 2*pad_x
	r.H = r.H - 2*pad_y

	r.X = r.X + pad_x
	r.Y = r.Y + pad_y

	w_half = r.W / 2.0
	h_half = r.H / 2.0

	if direction == Up {
		result[0] = image.Point{r.X + w_half, r.Y}
		result[1] = image.Point{r.X + r.W, r.Y + r.H}
		result[2] = image.Point{r.X, r.Y + r.H}
	} else if direction == Right {
		result[0] = image.Point{r.X, r.Y}
		result[1] = image.Point{r.X + r.W, r.Y + h_half}
		result[2] = image.Point{r.X, r.Y + r.H}
	} else if direction == Down {
		result[0] = image.Point{r.X, r.Y}
		result[1] = image.Point{r.X + r.W, r.Y}
		result[2] = image.Point{r.X + w_half, r.Y + r.H}
	} else {
		result[0] = image.Point{r.X, r.Y + h_half}
		result[1] = image.Point{r.X + r.W, r.Y}
		result[2] = image.Point{r.X + r.W, r.Y + r.H}
	}
	return
}

func minFloat(x, y float64) float64 {
	if x < y {
		return x
	}
	return y
}

func maxFloat(x, y float64) float64 {
	if x > y {
		return x
	}
	return y
}

func clampFloat(i, v, x float64) float64 {
	if v < i {
		v = i
	}
	if v > x {
		v = x
	}
	return v
}

func clampInt(i, v, x int) int {
	if v < i {
		v = i
	}
	if v > x {
		v = x
	}
	return v
}

func saturateFloat(x float64) float64 {
	return maxFloat(0.0, minFloat(1.0, x))
}

func basicWidgetStateControl(state *nstyle.WidgetStates, in *Input, bounds rect.Rect) nstyle.WidgetStates {
	if in == nil {
		*state = nstyle.WidgetStateInactive
		return nstyle.WidgetStateInactive
	}

	hovering := in.Mouse.HoveringRect(bounds)

	if *state == nstyle.WidgetStateInactive && hovering {
		*state = nstyle.WidgetStateHovered
	}

	if *state == nstyle.WidgetStateHovered && !hovering {
		*state = nstyle.WidgetStateInactive
	}

	if *state == nstyle.WidgetStateHovered && in.Mouse.HasClickInRect(mouse.ButtonLeft, bounds) {
		*state = nstyle.WidgetStateActive
	}

	if hovering {
		return nstyle.WidgetStateHovered
	} else {
		return nstyle.WidgetStateInactive
	}
}

func shrinkRect(r rect.Rect, amount int) rect.Rect {
	var res rect.Rect
	r.W = max(r.W, 2*amount)
	r.H = max(r.H, 2*amount)
	res.X = r.X + amount
	res.Y = r.Y + amount
	res.W = r.W - 2*amount
	res.H = r.H - 2*amount
	return res
}

func FontHeight(f font.Face) int {
	return f.Metrics().Ascent.Ceil() + f.Metrics().Descent.Ceil()
}

var fontWidthCache *lru.Cache

func init() {
	fontWidthCache, _ = lru.New(256)
}

type fontWidthCacheKey struct {
	f      font.Face
	string string
}

func FontWidth(f font.Face, str string) int {
	maxw := 0
	for {
		newline := strings.Index(str, "\n")
		line := str
		if newline >= 0 {
			line = str[:newline]
		}

		k := fontWidthCacheKey{f, line}

		var w int
		if val, ok := fontWidthCache.Get(k); ok {
			w = val.(int)
		} else {
			d := font.Drawer{Face: f}
			w = d.MeasureString(line).Ceil()
			fontWidthCache.Add(k, w)
		}

		if w > maxw {
			maxw = w
		}

		if newline >= 0 {
			str = str[newline+1:]
		} else {
			break
		}
	}
	return maxw
}

func unify(a rect.Rect, b rect.Rect) (clip rect.Rect) {
	clip.X = max(a.X, b.X)
	clip.Y = max(a.Y, b.Y)
	clip.W = min(a.X+a.W, b.X+b.W) - clip.X
	clip.H = min(a.Y+a.H, b.Y+b.H) - clip.Y
	clip.W = max(0.0, clip.W)
	clip.H = max(0.0, clip.H)
	return
}

func padRect(r rect.Rect, pad image.Point) rect.Rect {
	r.W = max(r.W, 2*pad.X)
	r.H = max(r.H, 2*pad.Y)
	r.X += pad.X
	r.Y += pad.Y
	r.W -= 2 * pad.X
	r.H -= 2 * pad.Y
	return r
}
<commit_msg>Adjustable font width cache<commit_after>package nucular

import (
	"image"
	"strings"

	nstyle "github.com/aarzilli/nucular/style"

	"golang.org/x/image/font"
	"golang.org/x/mobile/event/mouse"

	"github.com/aarzilli/nucular/rect"

	"github.com/hashicorp/golang-lru"
)

type Heading int

const (
	Up Heading = iota
	Right
	Down
	Left
)

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

func assert(cond bool) {
	if !cond {
		panic("assert!")
	}
}

func assert2(cond bool, reason string) {
	if !cond {
		panic(reason)
	}
}

func triangleFromDirection(r rect.Rect, pad_x, pad_y int, direction Heading) (result [3]image.Point) {
	var w_half int
	var h_half int

	r.W = max(2*pad_x, r.W)
	r.H = max(2*pad_y, r.H)
	r.W = r.W - 2*pad_x
	r.H = r.H - 2*pad_y

	r.X = r.X + pad_x
	r.Y = r.Y + pad_y

	w_half = r.W / 2.0
	h_half = r.H / 2.0

	if direction == Up {
		result[0] = image.Point{r.X + w_half, r.Y}
		result[1] = image.Point{r.X + r.W, r.Y + r.H}
		result[2] = image.Point{r.X, r.Y + r.H}
	} else if direction == Right {
		result[0] = image.Point{r.X, r.Y}
		result[1] = image.Point{r.X + r.W, r.Y + h_half}
		result[2] = image.Point{r.X, r.Y + r.H}
	} else if direction == Down {
		result[0] = image.Point{r.X, r.Y}
		result[1] = image.Point{r.X + r.W, r.Y}
		result[2] = image.Point{r.X + w_half, r.Y + r.H}
	} else {
		result[0] = image.Point{r.X, r.Y + h_half}
		result[1] = image.Point{r.X + r.W, r.Y}
		result[2] = image.Point{r.X + r.W, r.Y + r.H}
	}
	return
}

func minFloat(x, y float64) float64 {
	if x < y {
		return x
	}
	return y
}

func maxFloat(x, y float64) float64 {
	if x > y {
		return x
	}
	return y
}

func clampFloat(i, v, x float64) float64 {
	if v < i {
		v = i
	}
	if v > x {
		v = x
	}
	return v
}

func clampInt(i, v, x int) int {
	if v < i {
		v = i
	}
	if v > x {
		v = x
	}
	return v
}

func saturateFloat(x float64) float64 {
	return maxFloat(0.0, minFloat(1.0, x))
}

func basicWidgetStateControl(state *nstyle.WidgetStates, in *Input, bounds rect.Rect) nstyle.WidgetStates {
	if in == nil {
		*state = nstyle.WidgetStateInactive
		return nstyle.WidgetStateInactive
	}

	hovering := in.Mouse.HoveringRect(bounds)

	if *state == nstyle.WidgetStateInactive && hovering {
		*state = nstyle.WidgetStateHovered
	}

	if *state == nstyle.WidgetStateHovered && !hovering {
		*state = nstyle.WidgetStateInactive
	}

	if *state == nstyle.WidgetStateHovered && in.Mouse.HasClickInRect(mouse.ButtonLeft, bounds) {
		*state = nstyle.WidgetStateActive
	}

	if hovering {
		return nstyle.WidgetStateHovered
	} else {
		return nstyle.WidgetStateInactive
	}
}

func shrinkRect(r rect.Rect, amount int) rect.Rect {
	var res rect.Rect
	r.W = max(r.W, 2*amount)
	r.H = max(r.H, 2*amount)
	res.X = r.X + amount
	res.Y = r.Y + amount
	res.W = r.W - 2*amount
	res.H = r.H - 2*amount
	return res
}

func FontHeight(f font.Face) int {
	return f.Metrics().Ascent.Ceil() + f.Metrics().Descent.Ceil()
}

var fontWidthCache *lru.Cache
var fontWidthCacheSize int

func init() {
	fontWidthCacheSize = 256
	fontWidthCache, _ = lru.New(256)
}

func ChangeFontWidthCache(size int) {
	if size > fontWidthCacheSize {
		fontWidthCacheSize = size
		fontWidthCache, _ = lru.New(fontWidthCacheSize)
	}
}

type fontWidthCacheKey struct {
	f      font.Face
	string string
}

func FontWidth(f font.Face, str string) int {
	maxw := 0
	for {
		newline := strings.Index(str, "\n")
		line := str
		if newline >= 0 {
			line = str[:newline]
		}

		k := fontWidthCacheKey{f, line}

		var w int
		if val, ok := fontWidthCache.Get(k); ok {
			w = val.(int)
		} else {
			d := font.Drawer{Face: f}
			w = d.MeasureString(line).Ceil()
			fontWidthCache.Add(k, w)
		}

		if w > maxw {
			maxw = w
		}

		if newline >= 0 {
			str = str[newline+1:]
		} else {
			break
		}
	}
	return maxw
}

func unify(a rect.Rect, b rect.Rect) (clip rect.Rect) {
	clip.X = max(a.X, b.X)
	clip.Y = max(a.Y, b.Y)
	clip.W = min(a.X+a.W, b.X+b.W) - clip.X
	clip.H = min(a.Y+a.H, b.Y+b.H) - clip.Y
	clip.W = max(0.0, clip.W)
	clip.H = max(0.0, clip.H)
	return
}

func padRect(r rect.Rect, pad image.Point) rect.Rect {
	r.W = max(r.W, 2*pad.X)
	r.H = max(r.H, 2*pad.Y)
	r.X += pad.X
	r.Y += pad.Y
	r.W -= 2 * pad.X
	r.H -= 2 * pad.Y
	return r
}
<|endoftext|>