<commit_before>// Copyright © 2016 Aaron Longwell
//
// Use of this source code is governed by an MIT licese.
// Details in the LICENSE file.

package trello

import (
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/pkg/errors"
)

type Card struct {
	client *Client

	// Key metadata
	ID               string     `json:"id"`
	IDShort          int        `json:"idShort"`
	Name             string     `json:"name"`
	Pos              float64    `json:"pos"`
	Email            string     `json:"email"`
	ShortLink        string     `json:"shortLink"`
	ShortUrl         string     `json:"shortUrl"`
	Url              string     `json:"url"`
	Desc             string     `json:"desc"`
	Due              *time.Time `json:"due"`
	Closed           bool       `json:"closed"`
	Subscribed       bool       `json:"subscribed"`
	DateLastActivity *time.Time `json:"dateLastActivity"`

	// Board
	Board   *Board
	IDBoard string `json:"idBoard"`

	// List
	List   *List
	IDList string `json:"idList"`

	// Badges
	Badges struct {
		Votes              int        `json:"votes"`
		ViewingMemberVoted bool       `json:"viewingMemberVoted"`
		Subscribed         bool       `json:"subscribed"`
		Fogbugz            string     `json:"fogbugz,omitempty"`
		CheckItems         int        `json:"checkItems"`
		CheckItemsChecked  int        `json:"checkItemsChecked"`
		Comments           int        `json:"comments"`
		Attachments        int        `json:"attachments"`
		Description        bool       `json:"description"`
		Due                *time.Time `json:"due,omitempty"`
	} `json:"badges"`

	// Actions
	Actions ActionCollection `json:"actions,omitempty"`

	// Checklists
	IDCheckLists    []string          `json:"idCheckLists"`
	Checklists      []*Checklist      `json:"checklists,omitempty"`
	CheckItemStates []*CheckItemState `json:"checkItemStates,omitempty"`

	// Members
	IDMembers      []string  `json:"idMembers,omitempty"`
	IDMembersVoted []string  `json:"idMembersVoted,omitempty"`
	Members        []*Member `json:"members,omitempty"`

	// Attachments
	IDAttachmentCover     string        `json:"idAttachmentCover"`
	ManualCoverAttachment bool          `json:"manualCoverAttachment"`
	Attachments           []*Attachment `json:"attachments,omitempty"`

	// Labels
	Labels []*Label `json:"labels,omitempty"`
}

func (c *Card) CreatedAt() time.Time {
	t, _ := IDToTime(c.ID)
	return t
}

func (c *Card) MoveToList(listID string, args Arguments) error {
	path := fmt.Sprintf("cards/%s", c.ID)
	args["idList"] = listID
	return c.client.Put(path, args, &c)
}

func (c *Client) CreateCard(card *Card, extraArgs Arguments) error {
	path := "cards"
	args := Arguments{
		"name":      card.Name,
		"desc":      card.Desc,
		"pos":       strconv.FormatFloat(card.Pos, 'g', -1, 64),
		"idList":    card.IDList,
		"idMembers": strings.Join(card.IDMembers, ","),
	}
	if card.Due != nil {
		args["due"] = card.Due.Format(time.RFC3339)
	}
	// Allow overriding the creation position with 'top' or 'botttom'
	if pos, ok := extraArgs["pos"]; ok {
		args["pos"] = pos
	}
	err := c.Post(path, args, &card)
	if err == nil {
		card.client = c
	}
	return err
}

func (l *List) AddCard(card *Card, extraArgs Arguments) error {
	path := fmt.Sprintf("lists/%s/cards", l.ID)
	args := Arguments{
		"name":      card.Name,
		"desc":      card.Desc,
		"idMembers": strings.Join(card.IDMembers, ","),
	}
	if card.Due != nil {
		args["due"] = card.Due.Format(time.RFC3339)
	}
	// Allow overwriting the creation position with 'top' or 'bottom'
	if pos, ok := extraArgs["pos"]; ok {
		args["pos"] = pos
	}
	err := l.client.Post(path, args, &card)
	if err == nil {
		card.client = l.client
	} else {
		err = errors.Wrapf(err, "Error adding card to list %s", l.ID)
	}
	return err
}

// Try these Arguments
//
// 	Arguments["keepFromSource"] = "all"
//  Arguments["keepFromSource"] = "none"
// 	Arguments["keepFromSource"] = "attachments,checklists,comments"
//
func (c *Card) CopyToList(listID string, args Arguments) (*Card, error) {
	path := "cards"
	args["idList"] = listID
	args["idCardSource"] = c.ID
	newCard := Card{}
	err := c.client.Post(path, args, &newCard)
	if err == nil {
		newCard.client = c.client
	} else {
		err = errors.Wrapf(err, "Error copying card '%s' to list '%s'.", c.ID, listID)
	}
	return &newCard, err
}

func (c *Card) AddComment(comment string, args Arguments) (*Action, error) {
	path := fmt.Sprintf("cards/%s/actions/comments", c.ID)
	args["text"] = comment
	action := Action{}
	err := c.client.Post(path, args, &action)
	if err != nil {
		err = errors.Wrapf(err, "Error commenting on card %s", c.ID)
	}
	return &action, err
}

// If this Card was created from a copy of another Card, this func retrieves
// the originating Card. Returns an error only when a low-level failure occurred.
// If this Card has no parent, a nil card and nil error are returned. In other words, the
// non-existence of a parent is not treated as an error.
//
func (c *Card) GetParentCard(args Arguments) (*Card, error) {

	// Hopefully the card came pre-loaded with Actions including the card creation
	action := c.Actions.FirstCardCreateAction()

	if action == nil {
		// No luck. Go get copyCard actions for this card.
		c.client.Logger.Debugf("Creation action wasn't supplied before GetParentCard() on '%s'. Getting copyCard actions.", c.ID)
		actions, err := c.GetActions(Arguments{"filter": "copyCard"})
		if err != nil {
			err = errors.Wrapf(err, "GetParentCard() failed to GetActions() for card '%s'", c.ID)
			return nil, err
		}
		action = actions.FirstCardCreateAction()
	}

	if action != nil && action.Data != nil && action.Data.CardSource != nil {
		card, err := c.client.GetCard(action.Data.CardSource.ID, args)
		return card, err
	}

	return nil, nil
}

func (c *Card) GetAncestorCards(args Arguments) (ancestors []*Card, err error) {

	// Get the first parent
	parent, err := c.GetParentCard(args)
	if IsNotFound(err) || IsPermissionDenied(err) {
		c.client.Logger.Debugf("Can't get details about the parent of card '%s' due to lack of permissions or card deleted.", c.ID)
		return ancestors, nil
	}

	for parent != nil {
		ancestors = append(ancestors, parent)
		parent, err = parent.GetParentCard(args)
		if IsNotFound(err) || IsPermissionDenied(err) {
			c.client.Logger.Debugf("Can't get details about the parent of card '%s' due to lack of permissions or card deleted.", c.ID)
			return ancestors, nil
		} else if err != nil {
			return ancestors, err
		}
	}

	return ancestors, err
}

func (c *Card) GetOriginatingCard(args Arguments) (*Card, error) {
	ancestors, err := c.GetAncestorCards(args)
	if err != nil {
		return c, err
	}
	if len(ancestors) > 0 {
		return ancestors[len(ancestors)-1], nil
	} else {
		return c, nil
	}
}

func (c *Card) CreatorMemberID() (string, error) {

	var actions ActionCollection
	var err error

	if len(c.Actions) == 0 {
		c.client.Logger.Debugf("CreatorMemberID() called on card '%s' without any Card.Actions. Fetching fresh.", c.ID)
		actions, err = c.GetActions(Defaults())
		if err != nil {
			err = errors.Wrapf(err, "GetActions() call failed.")
		}
	} else {
		actions = c.Actions.FilterToCardCreationActions()
	}

	if len(actions) > 0 {
		if actions[0].IDMemberCreator != "" {
			return actions[0].IDMemberCreator, err
		}
	}

	return "", errors.Wrapf(err, "No Actions on card '%s' could be used to find its creator.", c.ID)
}

func (b *Board) ContainsCopyOfCard(cardID string, args Arguments) (bool, error) {
	args["filter"] = "copyCard"
	actions, err := b.GetActions(args)
	if err != nil {
		err := errors.Wrapf(err, "GetCards() failed inside ContainsCopyOf() for board '%s' and card '%s'.", b.ID, cardID)
		return false, err
	}
	for _, action := range actions {
		if action.Data != nil && action.Data.CardSource != nil && action.Data.CardSource.ID == cardID {
			return true, nil
		}
	}
	return false, nil
}

func (c *Client) GetCard(cardID string, args Arguments) (card *Card, err error) {
	path := fmt.Sprintf("cards/%s", cardID)
	err = c.Get(path, args, &card)
	if card != nil {
		card.client = c
	}
	return card, err
}

/**
 * Retrieves all Cards on a Board
 *
 * If before
 */
func (b *Board) GetCards(args Arguments) (cards []*Card, err error) {
	path := fmt.Sprintf("boards/%s/cards", b.ID)

	err = b.client.Get(path, args, &cards)

	// Naive implementation would return here. To make sure we get all
	// cards, we begin
	if len(cards) > 0 {
		moreCards := true
		for moreCards == true {
			nextCardBatch := make([]*Card, 0)
			args["before"] = EarliestCardID(cards)
			err = b.client.Get(path, args, &nextCardBatch)
			if len(nextCardBatch) > 0 {
				cards = append(cards, nextCardBatch...)
			} else {
				moreCards = false
			}
		}
	}

	for i := range cards {
		cards[i].client = b.client
	}

	return
}

/**
 * Retrieves all Cards in a List
 */
func (l *List) GetCards(args Arguments) (cards []*Card, err error) {
	path := fmt.Sprintf("lists/%s/cards", l.ID)
	err = l.client.Get(path, args, &cards)
	for i := range cards {
		cards[i].client = l.client
	}
	return
}

func EarliestCardID(cards []*Card) string {
	if len(cards) == 0 {
		return ""
	}
	earliest := cards[0].ID
	for _, card := range cards {
		if card.ID < earliest {
			earliest = card.ID
		}
	}
	return earliest
}
<commit_msg>Adds card.CreatorMember() which returns the full data from CreatorMemberID().<commit_after>// Copyright © 2016 Aaron Longwell
//
// Use of this source code is governed by an MIT licese.
// Details in the LICENSE file.

package trello

import (
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/pkg/errors"
)

type Card struct {
	client *Client

	// Key metadata
	ID               string     `json:"id"`
	IDShort          int        `json:"idShort"`
	Name             string     `json:"name"`
	Pos              float64    `json:"pos"`
	Email            string     `json:"email"`
	ShortLink        string     `json:"shortLink"`
	ShortUrl         string     `json:"shortUrl"`
	Url              string     `json:"url"`
	Desc             string     `json:"desc"`
	Due              *time.Time `json:"due"`
	Closed           bool       `json:"closed"`
	Subscribed       bool       `json:"subscribed"`
	DateLastActivity *time.Time `json:"dateLastActivity"`

	// Board
	Board   *Board
	IDBoard string `json:"idBoard"`

	// List
	List   *List
	IDList string `json:"idList"`

	// Badges
	Badges struct {
		Votes              int        `json:"votes"`
		ViewingMemberVoted bool       `json:"viewingMemberVoted"`
		Subscribed         bool       `json:"subscribed"`
		Fogbugz            string     `json:"fogbugz,omitempty"`
		CheckItems         int        `json:"checkItems"`
		CheckItemsChecked  int        `json:"checkItemsChecked"`
		Comments           int        `json:"comments"`
		Attachments        int        `json:"attachments"`
		Description        bool       `json:"description"`
		Due                *time.Time `json:"due,omitempty"`
	} `json:"badges"`

	// Actions
	Actions ActionCollection `json:"actions,omitempty"`

	// Checklists
	IDCheckLists    []string          `json:"idCheckLists"`
	Checklists      []*Checklist      `json:"checklists,omitempty"`
	CheckItemStates []*CheckItemState `json:"checkItemStates,omitempty"`

	// Members
	IDMembers      []string  `json:"idMembers,omitempty"`
	IDMembersVoted []string  `json:"idMembersVoted,omitempty"`
	Members        []*Member `json:"members,omitempty"`

	// Attachments
	IDAttachmentCover     string        `json:"idAttachmentCover"`
	ManualCoverAttachment bool          `json:"manualCoverAttachment"`
	Attachments           []*Attachment `json:"attachments,omitempty"`

	// Labels
	Labels []*Label `json:"labels,omitempty"`
}

func (c *Card) CreatedAt() time.Time {
	t, _ := IDToTime(c.ID)
	return t
}

func (c *Card) MoveToList(listID string, args Arguments) error {
	path := fmt.Sprintf("cards/%s", c.ID)
	args["idList"] = listID
	return c.client.Put(path, args, &c)
}

func (c *Client) CreateCard(card *Card, extraArgs Arguments) error {
	path := "cards"
	args := Arguments{
		"name":      card.Name,
		"desc":      card.Desc,
		"pos":       strconv.FormatFloat(card.Pos, 'g', -1, 64),
		"idList":    card.IDList,
		"idMembers": strings.Join(card.IDMembers, ","),
	}
	if card.Due != nil {
		args["due"] = card.Due.Format(time.RFC3339)
	}
	// Allow overriding the creation position with 'top' or 'botttom'
	if pos, ok := extraArgs["pos"]; ok {
		args["pos"] = pos
	}
	err := c.Post(path, args, &card)
	if err == nil {
		card.client = c
	}
	return err
}

func (l *List) AddCard(card *Card, extraArgs Arguments) error {
	path := fmt.Sprintf("lists/%s/cards", l.ID)
	args := Arguments{
		"name":      card.Name,
		"desc":      card.Desc,
		"idMembers": strings.Join(card.IDMembers, ","),
	}
	if card.Due != nil {
		args["due"] = card.Due.Format(time.RFC3339)
	}
	// Allow overwriting the creation position with 'top' or 'bottom'
	if pos, ok := extraArgs["pos"]; ok {
		args["pos"] = pos
	}
	err := l.client.Post(path, args, &card)
	if err == nil {
		card.client = l.client
	} else {
		err = errors.Wrapf(err, "Error adding card to list %s", l.ID)
	}
	return err
}

// Try these Arguments
//
// 	Arguments["keepFromSource"] = "all"
//  Arguments["keepFromSource"] = "none"
// 	Arguments["keepFromSource"] = "attachments,checklists,comments"
//
func (c *Card) CopyToList(listID string, args Arguments) (*Card, error) {
	path := "cards"
	args["idList"] = listID
	args["idCardSource"] = c.ID
	newCard := Card{}
	err := c.client.Post(path, args, &newCard)
	if err == nil {
		newCard.client = c.client
	} else {
		err = errors.Wrapf(err, "Error copying card '%s' to list '%s'.", c.ID, listID)
	}
	return &newCard, err
}

func (c *Card) AddComment(comment string, args Arguments) (*Action, error) {
	path := fmt.Sprintf("cards/%s/actions/comments", c.ID)
	args["text"] = comment
	action := Action{}
	err := c.client.Post(path, args, &action)
	if err != nil {
		err = errors.Wrapf(err, "Error commenting on card %s", c.ID)
	}
	return &action, err
}

// If this Card was created from a copy of another Card, this func retrieves
// the originating Card. Returns an error only when a low-level failure occurred.
// If this Card has no parent, a nil card and nil error are returned. In other words, the
// non-existence of a parent is not treated as an error.
//
func (c *Card) GetParentCard(args Arguments) (*Card, error) {

	// Hopefully the card came pre-loaded with Actions including the card creation
	action := c.Actions.FirstCardCreateAction()

	if action == nil {
		// No luck. Go get copyCard actions for this card.
		c.client.Logger.Debugf("Creation action wasn't supplied before GetParentCard() on '%s'. Getting copyCard actions.", c.ID)
		actions, err := c.GetActions(Arguments{"filter": "copyCard"})
		if err != nil {
			err = errors.Wrapf(err, "GetParentCard() failed to GetActions() for card '%s'", c.ID)
			return nil, err
		}
		action = actions.FirstCardCreateAction()
	}

	if action != nil && action.Data != nil && action.Data.CardSource != nil {
		card, err := c.client.GetCard(action.Data.CardSource.ID, args)
		return card, err
	}

	return nil, nil
}

func (c *Card) GetAncestorCards(args Arguments) (ancestors []*Card, err error) {

	// Get the first parent
	parent, err := c.GetParentCard(args)
	if IsNotFound(err) || IsPermissionDenied(err) {
		c.client.Logger.Debugf("Can't get details about the parent of card '%s' due to lack of permissions or card deleted.", c.ID)
		return ancestors, nil
	}

	for parent != nil {
		ancestors = append(ancestors, parent)
		parent, err = parent.GetParentCard(args)
		if IsNotFound(err) || IsPermissionDenied(err) {
			c.client.Logger.Debugf("Can't get details about the parent of card '%s' due to lack of permissions or card deleted.", c.ID)
			return ancestors, nil
		} else if err != nil {
			return ancestors, err
		}
	}

	return ancestors, err
}

func (c *Card) GetOriginatingCard(args Arguments) (*Card, error) {
	ancestors, err := c.GetAncestorCards(args)
	if err != nil {
		return c, err
	}
	if len(ancestors) > 0 {
		return ancestors[len(ancestors)-1], nil
	} else {
		return c, nil
	}
}

func (c *Card) CreatorMember() (*Member, error) {
	var actions ActionCollection
	var err error

	if len(c.Actions) == 0 {
		c.Actions, err = c.GetActions(Arguments{"filter": "all", "limit": "1000", "memberCreator_fields": "all"})
		if err != nil {
			err = errors.Wrapf(err, "GetActions() call failed.")
			return nil, err
		}
	}
	actions = c.Actions.FilterToCardCreationActions()

	if len(actions) > 0 {
		return actions[0].MemberCreator, nil
	}
	return nil, errors.Errorf("No card creation actions on Card %s with a .MemberCreator", c.ID)
}

func (c *Card) CreatorMemberID() (string, error) {

	var actions ActionCollection
	var err error

	if len(c.Actions) == 0 {
		c.client.Logger.Debugf("CreatorMemberID() called on card '%s' without any Card.Actions. Fetching fresh.", c.ID)
		c.Actions, err = c.GetActions(Defaults())
		if err != nil {
			err = errors.Wrapf(err, "GetActions() call failed.")
		}
	}
	actions = c.Actions.FilterToCardCreationActions()

	if len(actions) > 0 {
		if actions[0].IDMemberCreator != "" {
			return actions[0].IDMemberCreator, err
		}
	}

	return "", errors.Wrapf(err, "No Actions on card '%s' could be used to find its creator.", c.ID)
}

func (b *Board) ContainsCopyOfCard(cardID string, args Arguments) (bool, error) {
	args["filter"] = "copyCard"
	actions, err := b.GetActions(args)
	if err != nil {
		err := errors.Wrapf(err, "GetCards() failed inside ContainsCopyOf() for board '%s' and card '%s'.", b.ID, cardID)
		return false, err
	}
	for _, action := range actions {
		if action.Data != nil && action.Data.CardSource != nil && action.Data.CardSource.ID == cardID {
			return true, nil
		}
	}
	return false, nil
}

func (c *Client) GetCard(cardID string, args Arguments) (card *Card, err error) {
	path := fmt.Sprintf("cards/%s", cardID)
	err = c.Get(path, args, &card)
	if card != nil {
		card.client = c
	}
	return card, err
}

/**
 * Retrieves all Cards on a Board
 *
 * If before
 */
func (b *Board) GetCards(args Arguments) (cards []*Card, err error) {
	path := fmt.Sprintf("boards/%s/cards", b.ID)

	err = b.client.Get(path, args, &cards)

	// Naive implementation would return here. To make sure we get all
	// cards, we begin
	if len(cards) > 0 {
		moreCards := true
		for moreCards == true {
			nextCardBatch := make([]*Card, 0)
			args["before"] = EarliestCardID(cards)
			err = b.client.Get(path, args, &nextCardBatch)
			if len(nextCardBatch) > 0 {
				cards = append(cards, nextCardBatch...)
			} else {
				moreCards = false
			}
		}
	}

	for i := range cards {
		cards[i].client = b.client
	}

	return
}

/**
 * Retrieves all Cards in a List
 */
func (l *List) GetCards(args Arguments) (cards []*Card, err error) {
	path := fmt.Sprintf("lists/%s/cards", l.ID)
	err = l.client.Get(path, args, &cards)
	for i := range cards {
		cards[i].client = l.client
	}
	return
}

func EarliestCardID(cards []*Card) string {
	if len(cards) == 0 {
		return ""
	}
	earliest := cards[0].ID
	for _, card := range cards {
		if card.ID < earliest {
			earliest = card.ID
		}
	}
	return earliest
}
<|endoftext|>


<commit_before>// Package clac implements an RPN calculator.
package clac

import (
	"errors"

	"robpike.io/ivy/config"
	"robpike.io/ivy/exec"
	"robpike.io/ivy/value"
)

var (
	zero       value.Value = value.Int(0)
	E, Pi, Phi value.Value

	ErrTooFewArgs    = errors.New("too few arguments")
	ErrInvalidArg    = errors.New("invalid argument")
	ErrNoMoreChanges = errors.New("no more changes")
	ErrNoHistUpdate  = errors.New("") // for cmds that don't add to history

	ivyCfg = &config.Config{}
	ivyCtx = exec.NewContext(ivyCfg)
)

func init() {
	E, Pi = value.Consts(ivyCtx)
	e := &eval{}
	Phi = e.binary(e.binary(value.Int(1), "+", e.unary("sqrt", value.Int(5))), "/", value.Int(2))
}

// Sprint returns a stringified value
func Sprint(val value.Value) string {
	return val.Sprint(ivyCfg)
}

// SetFormat sets the ivy output format
func SetFormat(format string) {
	ivyCfg.SetFormat(format)
}

// ParseNum wraps value.Parse() to handle panics on unexpected input
func ParseNum(tok string) (val value.Value, err error) {
	defer func() {
		if recover() != nil {
			err = ErrInvalidArg
		}
	}()
	return value.Parse(ivyCfg, tok)
}

// Stack represents a stack of floating point numbers.
type Stack []value.Value

type stackHist struct {
	cur  int
	hist []Stack
}

func newStackHist() *stackHist {
	return &stackHist{hist: []Stack{Stack{}}}
}

func (s *stackHist) undo() bool {
	if s.cur <= 0 {
		return false
	}
	s.cur--
	return true
}

func (s *stackHist) redo() bool {
	if s.cur >= len(s.hist)-1 {
		return false
	}
	s.cur++
	return true
}

func (s *stackHist) push(stack Stack) {
	s.hist = append(s.hist[:s.cur+1], stack)
	s.cur++
}

func (s *stackHist) replace(stack Stack) {
	s.hist[s.cur] = stack
}

func (s *stackHist) stack() Stack {
	return s.hist[s.cur]
}

// Clac represents an RPN calculator.
type Clac struct {
	working  Stack
	keepHist bool
	hist     *stackHist
}

// New returns an initialized Clac instance.
func New() *Clac {
	c := &Clac{keepHist: true}
	c.Reset()
	return c
}

// EnableHistory sets whether to retain history
func (c *Clac) EnableHistory(enable bool) {
	c.keepHist = enable
	if !enable {
		c.hist = newStackHist()
	}
}

// Reset resets clac to its initial state
func (c *Clac) Reset() error {
	c.working = Stack{}
	c.hist = newStackHist()
	return ErrNoHistUpdate
}

// Stack returns the current stack.
func (c *Clac) Stack() Stack {
	return c.working
}

// Exec executes a clac command, along with necessary bookkeeping
func (c *Clac) Exec(f func() error) error {
	err := f()
	if err == nil {
		if c.keepHist {
			c.hist.push(c.working)
		} else {
			c.hist.replace(c.working)
		}
	}
	c.updateWorking()
	if err == ErrNoHistUpdate {
		return nil
	}
	return err
}

func (c *Clac) updateWorking() {
	c.working = append(Stack{}, c.hist.stack()...)
}

func (c *Clac) checkRange(pos, num int, isEndOK bool) (int, int, error) {
	max := len(c.working)
	if isEndOK {
		max++
	}
	start, end := pos, pos+num-1
	if start < 0 || start > end {
		return 0, 0, ErrInvalidArg
	}
	if start >= max || end >= max {
		return 0, 0, ErrTooFewArgs
	}
	return start, end, nil
}

func (c *Clac) insert(vals []value.Value, pos int) error {
	idx, _, err := c.checkRange(pos, 1, true)
	if err != nil {
		return err
	}
	c.working = append(c.working[:idx], append(vals, c.working[idx:]...)...)
	return nil
}

// Push pushes a value on the stack.
func (c *Clac) Push(x value.Value) error {
	return c.insert([]value.Value{x}, 0)
}

func (c *Clac) remove(pos, num int) ([]value.Value, error) {
	start, end, err := c.checkRange(pos, num, false)
	if err != nil {
		return nil, err
	}
	vals := append([]value.Value{}, c.working[start:end+1]...)
	c.working = append(c.working[:start], c.working[end+1:]...)
	return vals, nil
}

// Pop pops a value off the stack.
func (c *Clac) Pop() (value.Value, error) {
	x, err := c.remove(0, 1)
	if err != nil {
		return zero, err
	}
	return x[0], err
}

// Trunc returns the given value rounded to the nearest integer toward 0
func Trunc(val value.Value) (value.Value, error) {
	e := &eval{}
	if isTrue(e.binary(val, ">=", zero)) {
		val = e.unary("floor", val)
	} else {
		val = e.unary("ceil", val)
	}
	return val, e.err
}

func (c *Clac) popIntMin(min int) (int, error) {
	val, err := c.Pop()
	if err != nil {
		return 0, err
	}
	n, err := valToInt(val)
	if err != nil {
		return 0, err
	}
	if n < min {
		return 0, ErrInvalidArg
	}
	return n, nil
}

func valToInt(val value.Value) (int, error) {
	val, err := Trunc(val)
	if err != nil {
		return 0, err
	}
	ival, ok := val.(value.Int)
	if !ok {
		return 0, ErrInvalidArg
	}
	return int(ival), nil
}

func (c *Clac) popIndex() (int, error) {
	return c.popIntMin(0)
}

func (c *Clac) popCount() (int, error) {
	return c.popIntMin(1)
}

func (c *Clac) vals(pos, num int) (Stack, error) {
	start, end, err := c.checkRange(pos, num, false)
	if err != nil {
		return nil, err
	}
	return c.working[start : end+1], nil
}

func (c *Clac) dup(pos, num int) error {
	vals, err := c.vals(pos, num)
	if err != nil {
		return err
	}
	return c.insert(vals, 0)
}

func (c *Clac) drop(pos, num int) error {
	_, err := c.remove(pos, num)
	return err
}

func (c *Clac) rotate(pos, num int, isDown bool) error {
	from, to := 0, pos
	if isDown {
		from, to = to, from
	}
	vals, err := c.remove(from, num)
	if err != nil {
		return err
	}
	return c.insert(vals, to)
}

func unary(op string, a value.Value) (val value.Value, err error) {
	defer func() { err = errVal(recover()) }()
	val = value.Unary(ivyCtx, op, a)
	return val, err
}

func binary(a value.Value, op string, b value.Value) (val value.Value, err error) {
	defer func() { err = errVal(recover()) }()
	val = value.Binary(ivyCtx, a, op, b)
	return val, err
}

func errVal(val interface{}) error {
	if val == nil {
		return nil
	}
	if err, ok := val.(error); ok {
		return err
	}
	return errors.New("unknown error")
}

type eval struct {
	err error
}

func (e *eval) e(f func() (value.Value, error)) value.Value {
	if e.err != nil {
		return zero
	}
	var val value.Value
	val, e.err = f()
	return val
}

func (e *eval) unary(op string, a value.Value) value.Value {
	return e.e(func() (value.Value, error) { return unary(op, a) })
}

func (e *eval) binary(a value.Value, op string, b value.Value) value.Value {
	return e.e(func() (value.Value, error) { return binary(a, op, b) })
}
<commit_msg>Updated for ivy API change.<commit_after>// Package clac implements an RPN calculator.
package clac

import (
	"errors"

	"robpike.io/ivy/config"
	"robpike.io/ivy/exec"
	"robpike.io/ivy/value"
)

var (
	zero       value.Value = value.Int(0)
	E, Pi, Phi value.Value

	ErrTooFewArgs    = errors.New("too few arguments")
	ErrInvalidArg    = errors.New("invalid argument")
	ErrNoMoreChanges = errors.New("no more changes")
	ErrNoHistUpdate  = errors.New("") // for cmds that don't add to history

	ivyCfg = &config.Config{}
	ivyCtx = exec.NewContext(ivyCfg)
)

func init() {
	E, Pi = value.Consts(ivyCtx)
	e := &eval{}
	Phi = e.binary(e.binary(value.Int(1), "+", e.unary("sqrt", value.Int(5))), "/", value.Int(2))
}

// Sprint returns a stringified value
func Sprint(val value.Value) string {
	return val.Sprint(ivyCfg)
}

// SetFormat sets the ivy output format
func SetFormat(format string) {
	ivyCfg.SetFormat(format)
}

// ParseNum wraps value.Parse() to handle panics on unexpected input
func ParseNum(tok string) (val value.Value, err error) {
	defer func() {
		if recover() != nil {
			err = ErrInvalidArg
		}
	}()
	return value.Parse(ivyCfg, tok)
}

// Stack represents a stack of floating point numbers.
type Stack []value.Value

type stackHist struct {
	cur  int
	hist []Stack
}

func newStackHist() *stackHist {
	return &stackHist{hist: []Stack{Stack{}}}
}

func (s *stackHist) undo() bool {
	if s.cur <= 0 {
		return false
	}
	s.cur--
	return true
}

func (s *stackHist) redo() bool {
	if s.cur >= len(s.hist)-1 {
		return false
	}
	s.cur++
	return true
}

func (s *stackHist) push(stack Stack) {
	s.hist = append(s.hist[:s.cur+1], stack)
	s.cur++
}

func (s *stackHist) replace(stack Stack) {
	s.hist[s.cur] = stack
}

func (s *stackHist) stack() Stack {
	return s.hist[s.cur]
}

// Clac represents an RPN calculator.
type Clac struct {
	working  Stack
	keepHist bool
	hist     *stackHist
}

// New returns an initialized Clac instance.
func New() *Clac {
	c := &Clac{keepHist: true}
	c.Reset()
	return c
}

// EnableHistory sets whether to retain history
func (c *Clac) EnableHistory(enable bool) {
	c.keepHist = enable
	if !enable {
		c.hist = newStackHist()
	}
}

// Reset resets clac to its initial state
func (c *Clac) Reset() error {
	c.working = Stack{}
	c.hist = newStackHist()
	return ErrNoHistUpdate
}

// Stack returns the current stack.
func (c *Clac) Stack() Stack {
	return c.working
}

// Exec executes a clac command, along with necessary bookkeeping
func (c *Clac) Exec(f func() error) error {
	err := f()
	if err == nil {
		if c.keepHist {
			c.hist.push(c.working)
		} else {
			c.hist.replace(c.working)
		}
	}
	c.updateWorking()
	if err == ErrNoHistUpdate {
		return nil
	}
	return err
}

func (c *Clac) updateWorking() {
	c.working = append(Stack{}, c.hist.stack()...)
}

func (c *Clac) checkRange(pos, num int, isEndOK bool) (int, int, error) {
	max := len(c.working)
	if isEndOK {
		max++
	}
	start, end := pos, pos+num-1
	if start < 0 || start > end {
		return 0, 0, ErrInvalidArg
	}
	if start >= max || end >= max {
		return 0, 0, ErrTooFewArgs
	}
	return start, end, nil
}

func (c *Clac) insert(vals []value.Value, pos int) error {
	idx, _, err := c.checkRange(pos, 1, true)
	if err != nil {
		return err
	}
	c.working = append(c.working[:idx], append(vals, c.working[idx:]...)...)
	return nil
}

// Push pushes a value on the stack.
func (c *Clac) Push(x value.Value) error {
	return c.insert([]value.Value{x}, 0)
}

func (c *Clac) remove(pos, num int) ([]value.Value, error) {
	start, end, err := c.checkRange(pos, num, false)
	if err != nil {
		return nil, err
	}
	vals := append([]value.Value{}, c.working[start:end+1]...)
	c.working = append(c.working[:start], c.working[end+1:]...)
	return vals, nil
}

// Pop pops a value off the stack.
func (c *Clac) Pop() (value.Value, error) {
	x, err := c.remove(0, 1)
	if err != nil {
		return zero, err
	}
	return x[0], err
}

// Trunc returns the given value rounded to the nearest integer toward 0
func Trunc(val value.Value) (value.Value, error) {
	e := &eval{}
	if isTrue(e.binary(val, ">=", zero)) {
		val = e.unary("floor", val)
	} else {
		val = e.unary("ceil", val)
	}
	return val, e.err
}

func (c *Clac) popIntMin(min int) (int, error) {
	val, err := c.Pop()
	if err != nil {
		return 0, err
	}
	n, err := valToInt(val)
	if err != nil {
		return 0, err
	}
	if n < min {
		return 0, ErrInvalidArg
	}
	return n, nil
}

func valToInt(val value.Value) (int, error) {
	val, err := Trunc(val)
	if err != nil {
		return 0, err
	}
	ival, ok := val.(value.Int)
	if !ok {
		return 0, ErrInvalidArg
	}
	return int(ival), nil
}

func (c *Clac) popIndex() (int, error) {
	return c.popIntMin(0)
}

func (c *Clac) popCount() (int, error) {
	return c.popIntMin(1)
}

func (c *Clac) vals(pos, num int) (Stack, error) {
	start, end, err := c.checkRange(pos, num, false)
	if err != nil {
		return nil, err
	}
	return c.working[start : end+1], nil
}

func (c *Clac) dup(pos, num int) error {
	vals, err := c.vals(pos, num)
	if err != nil {
		return err
	}
	return c.insert(vals, 0)
}

func (c *Clac) drop(pos, num int) error {
	_, err := c.remove(pos, num)
	return err
}

func (c *Clac) rotate(pos, num int, isDown bool) error {
	from, to := 0, pos
	if isDown {
		from, to = to, from
	}
	vals, err := c.remove(from, num)
	if err != nil {
		return err
	}
	return c.insert(vals, to)
}

func unary(op string, a value.Value) (val value.Value, err error) {
	defer func() { err = errVal(recover()) }()
	val = ivyCtx.EvalUnary(op, a)
	return val, err
}

func binary(a value.Value, op string, b value.Value) (val value.Value, err error) {
	defer func() { err = errVal(recover()) }()
	val = ivyCtx.EvalBinary(a, op, b)
	return val, err
}

func errVal(val interface{}) error {
	if val == nil {
		return nil
	}
	if err, ok := val.(error); ok {
		return err
	}
	return errors.New("unknown error")
}

type eval struct {
	err error
}

func (e *eval) e(f func() (value.Value, error)) value.Value {
	if e.err != nil {
		return zero
	}
	var val value.Value
	val, e.err = f()
	return val
}

func (e *eval) unary(op string, a value.Value) value.Value {
	return e.e(func() (value.Value, error) { return unary(op, a) })
}

func (e *eval) binary(a value.Value, op string, b value.Value) value.Value {
	return e.e(func() (value.Value, error) { return binary(a, op, b) })
}
<|endoftext|>


<commit_before>package rhynock

import (
	"github.com/gorilla/websocket"
	"net/http"
	"time"
	"log"
)

// Connection handles our websocket
type Connection struct {
	Ws      *websocket.Conn
	Send    chan []byte
	Authed  chan bool
	Dst     BottleDst
}

const (
	writeWait = 10 * time.Second
	pongWait = 60 * time.Second
	pingPeriod = (pongWait * 9) / 10
	maxMessageSize = 512
)

//
// Used to write a single message to the client and report any errors
//
func (c *Connection) write(t int, payload []byte) error {
	c.Ws.SetWriteDeadline(time.Now().Add(writeWait))
	return c.Ws.WriteMessage(t, payload)
}

//
// Maintains both a reader and a writer, cleans up both if one fails
//
func (c *Connection) read_write() {
	// Ping timer
	ticker := time.NewTicker(pingPeriod)

	// Clean up Connection and Connection resources
	defer func() {
		ticker.Stop()
		c.Ws.Close()
	}()

	// Config websocket settings
	c.Ws.SetReadLimit(maxMessageSize)
	c.Ws.SetReadDeadline(time.Now().Add(pongWait))
	c.Ws.SetPongHandler(func(string) error {
		// Give each client pongWait seconds after the ping to respond
		c.Ws.SetReadDeadline(time.Now().Add(pongWait))
		return nil
	})

	// Start a reading goroutine
	// The reader will stop when the c.Ws.Close is called at
	// in the defered cleanup function, so we do not manually
	// have to close the reader
	go func() {
		for {
			// This blcoks until it reads EOF or an error
			// occurs trying to read, the error can be
			// used to detect when the client closes the Connection
			_, message, err := c.Ws.ReadMessage()
			if err != nil {
				break // If we get an error escape the loop
			}

			// Bottle the message with its sender
			bottle := &Bottle{
				Sender: c,
				Message: message,
			}

			// Send to the destination for processing
			c.Dst.GetBottleChan() <- bottle
		}
		// The reader has been terminated

	}()

	// Main handling loop
	for {
		select {
		case message, ok := <- c.Send:
			// Our send channel has something in it or the channel closed
			if !ok {
				// Our channel was closed, gracefully close socket Connection
				c.write(websocket.CloseMessage, []byte{})
				return
			}
			// Attempt to write the message to the websocket
			if err := c.write(websocket.TextMessage, message); err != nil {
				// If we get an error we can no longer communcate with client
				// return, no need to send CloseMessage since that would
				// just yield another error
				return
			}

		case <- ticker.C:
			// Ping ticker went off. We need to ping to check for connectivity.
			if err := c.write(websocket.PingMessage, []byte{}); err != nil {
				// We got an error pinging, return and call defer
				// defer will close the socket which will kill the reader
				return
			}
		}
	}

}

var upgrader = &websocket.Upgrader{ReadBufferSize: 1024, WriteBufferSize: 1024, CheckOrigin: func(r* http.Request) bool { return true }}

func ConnectionHandler(w http.ResponseWriter, r *http.Request, dst BottleDst) {
	ws, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Println(err)
		return
	}

	// Create new connection object
	c := &Connection{
		Send: make(chan []byte, 256),
		Ws: ws,
		Authed: make(chan bool),
		Dst: dst,
	}

	// Start infinite read/write loop
	c.read_write()
}
<commit_msg>Send new connection to the dst<commit_after>package rhynock

import (
	"github.com/gorilla/websocket"
	"net/http"
	"time"
	"log"
)

// Connection handles our websocket
type Connection struct {
	Ws      *websocket.Conn
	Send    chan []byte
	Authed  chan bool
	Dst     BottleDst
}

const (
	writeWait = 10 * time.Second
	pongWait = 60 * time.Second
	pingPeriod = (pongWait * 9) / 10
	maxMessageSize = 512
)

//
// Used to write a single message to the client and report any errors
//
func (c *Connection) write(t int, payload []byte) error {
	c.Ws.SetWriteDeadline(time.Now().Add(writeWait))
	return c.Ws.WriteMessage(t, payload)
}

//
// Maintains both a reader and a writer, cleans up both if one fails
//
func (c *Connection) read_write() {
	// Ping timer
	ticker := time.NewTicker(pingPeriod)

	// Clean up Connection and Connection resources
	defer func() {
		ticker.Stop()
		c.Ws.Close()
	}()

	// Config websocket settings
	c.Ws.SetReadLimit(maxMessageSize)
	c.Ws.SetReadDeadline(time.Now().Add(pongWait))
	c.Ws.SetPongHandler(func(string) error {
		// Give each client pongWait seconds after the ping to respond
		c.Ws.SetReadDeadline(time.Now().Add(pongWait))
		return nil
	})

	// Start a reading goroutine
	// The reader will stop when the c.Ws.Close is called at
	// in the defered cleanup function, so we do not manually
	// have to close the reader
	go func() {
		for {
			// This blcoks until it reads EOF or an error
			// occurs trying to read, the error can be
			// used to detect when the client closes the Connection
			_, message, err := c.Ws.ReadMessage()
			if err != nil {
				break // If we get an error escape the loop
			}

			// Bottle the message with its sender
			bottle := &Bottle{
				Sender: c,
				Message: message,
			}

			// Send to the destination for processing
			c.Dst.GetBottleChan() <- bottle
		}
		// The reader has been terminated

	}()

	// Main handling loop
	for {
		select {
		case message, ok := <- c.Send:
			// Our send channel has something in it or the channel closed
			if !ok {
				// Our channel was closed, gracefully close socket Connection
				c.write(websocket.CloseMessage, []byte{})
				return
			}
			// Attempt to write the message to the websocket
			if err := c.write(websocket.TextMessage, message); err != nil {
				// If we get an error we can no longer communcate with client
				// return, no need to send CloseMessage since that would
				// just yield another error
				return
			}

		case <- ticker.C:
			// Ping ticker went off. We need to ping to check for connectivity.
			if err := c.write(websocket.PingMessage, []byte{}); err != nil {
				// We got an error pinging, return and call defer
				// defer will close the socket which will kill the reader
				return
			}
		}
	}

}

var upgrader = &websocket.Upgrader{ReadBufferSize: 1024, WriteBufferSize: 1024, CheckOrigin: func(r* http.Request) bool { return true }}

func ConnectionHandler(w http.ResponseWriter, r *http.Request, dst BottleDst) {
	ws, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Println(err)
		return
	}

	// Create new connection object
	c := &Connection{
		Send: make(chan []byte, 256),
		Ws: ws,
		Authed: make(chan bool),
		Dst: dst,
	}

	dst.ConnectionOpened(c)

	// Start infinite read/write loop
	c.read_write()
}
<|endoftext|>


<commit_before>package neptulon

import (
	"errors"
	"io"
	"log"
	"net"
	"runtime"
	"sync"
	"time"

	"github.com/neptulon/cmap"
	"github.com/neptulon/shortid"

	"golang.org/x/net/websocket"
)

// Conn is a client connection.
type Conn struct {
	ID           string
	Session      *cmap.CMap
	middleware   []func(ctx *ReqCtx) error
	resRoutes    *cmap.CMap // message ID (string) -> handler func(ctx *ResCtx) error : expected responses for requests that we've sent
	ws           *websocket.Conn
	wg           sync.WaitGroup
	deadline     time.Duration
	isClientConn bool
	connected    bool
}

// NewConn creates a new Conn object.
func NewConn() (*Conn, error) {
	id, err := shortid.UUID()
	if err != nil {
		return nil, err
	}

	return &Conn{
		ID:        id,
		Session:   cmap.New(),
		resRoutes: cmap.New(),
		deadline:  time.Second * time.Duration(300),
	}, nil
}

// SetDeadline set the read/write deadlines for the connection, in seconds.
// Default value for read/write deadline is 300 seconds.
func (c *Conn) SetDeadline(seconds int) {
	c.deadline = time.Second * time.Duration(seconds)
}

// Middleware registers middleware to handle incoming request messages.
func (c *Conn) Middleware(middleware ...func(ctx *ReqCtx) error) {
	c.middleware = append(c.middleware, middleware...)
}

// Connect connects to the given WebSocket server.
func (c *Conn) Connect(addr string) error {
	ws, err := websocket.Dial(addr, "", "http://localhost")
	if err != nil {
		return err
	}

	c.ws = ws
	c.connected = true
	c.isClientConn = true
	c.wg.Add(1)
	go func() {
		defer recoverAndLog(c, &c.wg)
		c.startReceive()
	}()
	time.Sleep(time.Millisecond) // give receive goroutine a few cycles to start
	return nil
}

// RemoteAddr returns the remote network address.
func (c *Conn) RemoteAddr() net.Addr {
	if c.ws == nil {
		return nil
	}

	return c.ws.RemoteAddr()
}

// SendRequest sends a JSON-RPC request through the connection with an auto generated request ID.
// resHandler is called when a response is returned.
func (c *Conn) SendRequest(method string, params interface{}, resHandler func(res *ResCtx) error) (reqID string, err error) {
	id, err := shortid.UUID()
	if err != nil {
		return "", err
	}

	req := request{ID: id, Method: method, Params: params}
	if err = c.send(req); err != nil {
		return "", err
	}

	c.resRoutes.Set(req.ID, resHandler)
	return id, nil
}

// SendRequestArr sends a JSON-RPC request through the connection, with array params and auto generated request ID.
// resHandler is called when a response is returned.
func (c *Conn) SendRequestArr(method string, resHandler func(res *ResCtx) error, params ...interface{}) (reqID string, err error) {
	return c.SendRequest(method, params, resHandler)
}

// Close closes the connection.
func (c *Conn) Close() error {
	c.connected = false
	return c.ws.Close()
}

// SendResponse sends a JSON-RPC response message through the connection.
func (c *Conn) sendResponse(id string, result interface{}, err *ResError) error {
	return c.send(response{ID: id, Result: result, Error: err})
}

// Send sends the given message through the connection.
func (c *Conn) send(msg interface{}) error {
	if !c.connected {
		return errors.New("use of closed connection")
	}

	if err := c.ws.SetWriteDeadline(time.Now().Add(c.deadline)); err != nil {
		return err
	}

	return websocket.JSON.Send(c.ws, msg)
}

// Receive receives message from the connection.
func (c *Conn) receive(msg *message) error {
	if !c.connected {
		return errors.New("use of closed connection")
	}

	if err := c.ws.SetReadDeadline(time.Now().Add(c.deadline)); err != nil {
		return err
	}

	return websocket.JSON.Receive(c.ws, &msg)
}

// UseConn reuses an established websocket.Conn.
// This function blocks and does not return until the connection is closed by another goroutine.
func (c *Conn) useConn(ws *websocket.Conn) {
	c.ws = ws
	c.connected = true
	c.startReceive()
}

// startReceive starts receiving messages. This method blocks and does not return until the connection is closed.
func (c *Conn) startReceive() {
	defer c.Close()

	for {
		var m message
		err := c.receive(&m)
		if err != nil {
			// if we closed the connection
			if !c.connected {
				log.Printf("conn: closed %v: %v", c.ID, c.RemoteAddr())
				break
			}

			// if peer closed the connection
			if err == io.EOF {
				log.Printf("conn: peer disconnected %v: %v", c.ID, c.RemoteAddr())
				break
			}

			log.Printf("conn: error while receiving message: %v", err)
			break
		}

		// if the message is a request
		if m.Method != "" {
			c.wg.Add(1)
			go func() {
				defer recoverAndLog(c, &c.wg)
				if err := newReqCtx(c, m.ID, m.Method, m.Params, c.middleware).Next(); err != nil {
					log.Printf("conn: error while handling request: %v", err)
					c.Close()
				}
			}()

			continue
		}

		// if the message is not a JSON-RPC message
		if m.ID == "" || (m.Result == nil && m.Error == nil) {
			log.Printf("conn: received an unknown message %v: %v\n%v", c.ID, c.RemoteAddr(), m)
			break
		}

		// if the message is a response
		if resHandler, ok := c.resRoutes.GetOk(m.ID); ok {
			c.wg.Add(1)
			go func() {
				defer recoverAndLog(c, &c.wg)
				err := resHandler.(func(ctx *ResCtx) error)(newResCtx(c, m.ID, m.Result, m.Error))
				c.resRoutes.Delete(m.ID)
				if err != nil {
					log.Printf("conn: error while handling response: %v", err)
					c.Close()
				}
			}()
		} else {
			log.Printf("conn: error while handling response: got response to a request with unknown ID: %v", m.ID)
			break
		}
	}
}

func recoverAndLog(c *Conn, wg *sync.WaitGroup) {
	defer wg.Done()
	if err := recover(); err != nil {
		const size = 64 << 10
		buf := make([]byte, size)
		buf = buf[:runtime.Stack(buf, false)]
		log.Printf("conn: panic handling response %v: %v\n%s", c.RemoteAddr(), err, buf)
	}
}
<commit_msg>synchronize access to Conn.connected<commit_after>package neptulon

import (
	"errors"
	"io"
	"log"
	"net"
	"runtime"
	"sync"
	"time"

	"github.com/neptulon/cmap"
	"github.com/neptulon/shortid"

	"golang.org/x/net/websocket"
)

// Conn is a client connection.
type Conn struct {
	ID             string
	Session        *cmap.CMap
	middleware     []func(ctx *ReqCtx) error
	resRoutes      *cmap.CMap // message ID (string) -> handler func(ctx *ResCtx) error : expected responses for requests that we've sent
	ws             *websocket.Conn
	wg             sync.WaitGroup
	deadline       time.Duration
	isClientConn   bool
	connectedMutex sync.RWMutex
	connected      bool
}

// NewConn creates a new Conn object.
func NewConn() (*Conn, error) {
	id, err := shortid.UUID()
	if err != nil {
		return nil, err
	}

	return &Conn{
		ID:        id,
		Session:   cmap.New(),
		resRoutes: cmap.New(),
		deadline:  time.Second * time.Duration(300),
	}, nil
}

// SetDeadline set the read/write deadlines for the connection, in seconds.
// Default value for read/write deadline is 300 seconds.
func (c *Conn) SetDeadline(seconds int) {
	c.deadline = time.Second * time.Duration(seconds)
}

// Middleware registers middleware to handle incoming request messages.
func (c *Conn) Middleware(middleware ...func(ctx *ReqCtx) error) {
	c.middleware = append(c.middleware, middleware...)
}

// Connect connects to the given WebSocket server.
func (c *Conn) Connect(addr string) error {
	ws, err := websocket.Dial(addr, "", "http://localhost")
	if err != nil {
		return err
	}

	c.ws = ws
	c.connectedMutex.Lock()
	c.connected = true
	c.connectedMutex.Unlock()
	c.isClientConn = true
	c.wg.Add(1)
	go func() {
		defer recoverAndLog(c, &c.wg)
		c.startReceive()
	}()
	time.Sleep(time.Millisecond) // give receive goroutine a few cycles to start
	return nil
}

// RemoteAddr returns the remote network address.
func (c *Conn) RemoteAddr() net.Addr {
	if c.ws == nil {
		return nil
	}

	return c.ws.RemoteAddr()
}

// SendRequest sends a JSON-RPC request through the connection with an auto generated request ID.
// resHandler is called when a response is returned.
func (c *Conn) SendRequest(method string, params interface{}, resHandler func(res *ResCtx) error) (reqID string, err error) {
	id, err := shortid.UUID()
	if err != nil {
		return "", err
	}

	req := request{ID: id, Method: method, Params: params}
	if err = c.send(req); err != nil {
		return "", err
	}

	c.resRoutes.Set(req.ID, resHandler)
	return id, nil
}

// SendRequestArr sends a JSON-RPC request through the connection, with array params and auto generated request ID.
// resHandler is called when a response is returned.
func (c *Conn) SendRequestArr(method string, resHandler func(res *ResCtx) error, params ...interface{}) (reqID string, err error) {
	return c.SendRequest(method, params, resHandler)
}

// Close closes the connection.
func (c *Conn) Close() error {
	c.connectedMutex.Lock()
	c.connected = false
	c.connectedMutex.Unlock()
	return c.ws.Close()
}

// SendResponse sends a JSON-RPC response message through the connection.
func (c *Conn) sendResponse(id string, result interface{}, err *ResError) error {
	return c.send(response{ID: id, Result: result, Error: err})
}

// Send sends the given message through the connection.
func (c *Conn) send(msg interface{}) error {
	c.connectedMutex.RLock()
	ct := c.connected
	c.connectedMutex.RUnlock()
	if !ct {
		return errors.New("use of closed connection")
	}

	if err := c.ws.SetWriteDeadline(time.Now().Add(c.deadline)); err != nil {
		return err
	}

	return websocket.JSON.Send(c.ws, msg)
}

// Receive receives message from the connection.
func (c *Conn) receive(msg *message) error {
	c.connectedMutex.RLock()
	ct := c.connected
	c.connectedMutex.RUnlock()
	if !ct {
		return errors.New("use of closed connection")
	}

	if err := c.ws.SetReadDeadline(time.Now().Add(c.deadline)); err != nil {
		return err
	}

	return websocket.JSON.Receive(c.ws, &msg)
}

// UseConn reuses an established websocket.Conn.
// This function blocks and does not return until the connection is closed by another goroutine.
func (c *Conn) useConn(ws *websocket.Conn) {
	c.ws = ws
	c.connectedMutex.Lock()
	c.connected = true
	c.connectedMutex.Unlock()
	c.startReceive()
}

// startReceive starts receiving messages. This method blocks and does not return until the connection is closed.
func (c *Conn) startReceive() {
	defer c.Close()

	for {
		var m message
		err := c.receive(&m)
		if err != nil {
			// if we closed the connection
			c.connectedMutex.RLock()
			ct := c.connected
			c.connectedMutex.RUnlock()
			if !ct {
				log.Printf("conn: closed %v: %v", c.ID, c.RemoteAddr())
				break
			}

			// if peer closed the connection
			if err == io.EOF {
				log.Printf("conn: peer disconnected %v: %v", c.ID, c.RemoteAddr())
				break
			}

			log.Printf("conn: error while receiving message: %v", err)
			break
		}

		// if the message is a request
		if m.Method != "" {
			c.wg.Add(1)
			go func() {
				defer recoverAndLog(c, &c.wg)
				if err := newReqCtx(c, m.ID, m.Method, m.Params, c.middleware).Next(); err != nil {
					log.Printf("conn: error while handling request: %v", err)
					c.Close()
				}
			}()

			continue
		}

		// if the message is not a JSON-RPC message
		if m.ID == "" || (m.Result == nil && m.Error == nil) {
			log.Printf("conn: received an unknown message %v: %v\n%v", c.ID, c.RemoteAddr(), m)
			break
		}

		// if the message is a response
		if resHandler, ok := c.resRoutes.GetOk(m.ID); ok {
			c.wg.Add(1)
			go func() {
				defer recoverAndLog(c, &c.wg)
				err := resHandler.(func(ctx *ResCtx) error)(newResCtx(c, m.ID, m.Result, m.Error))
				c.resRoutes.Delete(m.ID)
				if err != nil {
					log.Printf("conn: error while handling response: %v", err)
					c.Close()
				}
			}()
		} else {
			log.Printf("conn: error while handling response: got response to a request with unknown ID: %v", m.ID)
			break
		}
	}
}

func recoverAndLog(c *Conn, wg *sync.WaitGroup) {
	defer wg.Done()
	if err := recover(); err != nil {
		const size = 64 << 10
		buf := make([]byte, size)
		buf = buf[:runtime.Stack(buf, false)]
		log.Printf("conn: panic handling response %v: %v\n%s", c.RemoteAddr(), err, buf)
	}
}
<|endoftext|>


<commit_before>package epp

import (
	"bytes"
	"encoding/binary"
	"encoding/xml"
	"io"
	"net"
	"time"
)

// IgnoreEOF returns err unless err == io.EOF,
// in which case it returns nil.
func IgnoreEOF(err error) error {
	if err == io.EOF {
		return nil
	}
	return err
}

// Conn represents a single connection to an EPP server.
// This implementation is not safe for concurrent use.
type Conn struct {
	net.Conn
	buf     bytes.Buffer
	decoder *xml.Decoder
	saved   xml.Decoder

	// Greeting holds the last received greeting message from the server,
	// indicating server name, status, data policy and capabilities.
	Greeting

	// LoginResult holds the last received login response message's Result
	// from the server, in which some servers might include diagnostics such
	// as connection count limits.
	LoginResult Result

	// Timeout defines the timeout for network operations.
	Timeout time.Duration
}

// NewConn initializes an epp.Conn from a net.Conn and performs the EPP
// handshake. It reads and stores the initial EPP <greeting> message.
// https://tools.ietf.org/html/rfc5730#section-2.4
func NewConn(conn net.Conn) (*Conn, error) {
	c := newConn(conn)
	g, err := c.readGreeting()
	if err == nil {
		c.Greeting = g
	}
	return c, err
}

// NewTimeoutConn initializes an epp.Conn like NewConn, limiting the duration of network
// operations on conn using Set(Read|Write)Deadline.
func NewTimeoutConn(conn net.Conn, timeout time.Duration) (*Conn, error) {
	c := newConn(conn)
	c.Timeout = timeout
	g, err := c.readGreeting()
	if err == nil {
		c.Greeting = g
	}
	return c, err
}

// Close sends an EPP <logout> command and closes the connection c.
func (c *Conn) Close() error {
	c.Logout()
	return c.Conn.Close()
}

// newConn initializes an epp.Conn from a net.Conn.
// Used internally for testing.
func newConn(conn net.Conn) *Conn {
	c := Conn{Conn: conn}
	c.decoder = xml.NewDecoder(&c.buf)
	c.saved = *c.decoder
	return &c
}

// reset resets the underlying xml.Decoder and bytes.Buffer,
// restoring the original state of the underlying
// xml.Decoder (pos 1, line 1, stack, etc.) using a hack.
func (c *Conn) reset() {
	c.buf.Reset()
	*c.decoder = c.saved // Heh.
}

// flushDataUnit writes bytes from c.buf to c using writeDataUnit.
func (c *Conn) flushDataUnit() error {
	return c.writeDataUnit(c.buf.Bytes())
}

// writeDataUnit writes a slice of bytes to c.
// Bytes written are prefixed with 32-bit header specifying the total size
// of the data unit (message + 4 byte header), in network (big-endian) order.
// http://www.ietf.org/rfc/rfc4934.txt
func (c *Conn) writeDataUnit(x []byte) error {
	logXML("<-- WRITE DATA UNIT -->", x)
	s := uint32(4 + len(x))
	if c.Timeout > 0 {
		c.Conn.SetWriteDeadline(time.Now().Add(c.Timeout))
	}
	err := binary.Write(c.Conn, binary.BigEndian, s)
	if err != nil {
		return err
	}
	_, err = c.Conn.Write(x)
	return err
}

// readResponse reads a single EPP response from c and parses the XML into req.
// It returns an error if the EPP response contains an error Result.
func (c *Conn) readResponse(res *Response) error {
	err := c.readDataUnit()
	if err != nil {
		return err
	}
	err = IgnoreEOF(scanResponse.Scan(c.decoder, res))
	if err != nil {
		return err
	}
	if res.Result.IsError() {
		return &res.Result
	}
	return nil
}

// readDataUnit reads a single EPP message from c into
// c.buf. The bytes in c.buf are valid until the next
// call to readDataUnit.
func (c *Conn) readDataUnit() error {
	c.reset()
	var s int32
	if c.Timeout > 0 {
		c.Conn.SetReadDeadline(time.Now().Add(c.Timeout))
	}
	err := binary.Read(c.Conn, binary.BigEndian, &s)
	if err != nil {
		return err
	}
	s -= 4 // https://tools.ietf.org/html/rfc5734#section-4
	if s < 0 {
		return io.ErrUnexpectedEOF
	}
	lr := io.LimitedReader{R: c.Conn, N: int64(s)}
	n, err := c.buf.ReadFrom(&lr)
	if err != nil {
		return err
	}
	if n != int64(s) || lr.N != 0 {
		return io.ErrUnexpectedEOF
	}
	logXML("<-- READ DATA UNIT -->", c.buf.Bytes())
	return nil
}

func deleteRange(s, pfx, sfx []byte) []byte {
	start := bytes.Index(s, pfx)
	if start < 0 {
		return s
	}
	end := bytes.Index(s[start+len(pfx):], sfx)
	if end < 0 {
		return s
	}
	end += start + len(pfx) + len(sfx)
	size := len(s) - (end - start)
	copy(s[start:size], s[end:])
	return s[:size]
}

func deleteBufferRange(buf *bytes.Buffer, pfx, sfx []byte) {
	v := deleteRange(buf.Bytes(), pfx, sfx)
	buf.Truncate(len(v))
}
<commit_msg>remove (*Conn).flushDataUnit method<commit_after>package epp

import (
	"bytes"
	"encoding/binary"
	"encoding/xml"
	"io"
	"net"
	"time"
)

// IgnoreEOF returns err unless err == io.EOF,
// in which case it returns nil.
func IgnoreEOF(err error) error {
	if err == io.EOF {
		return nil
	}
	return err
}

// Conn represents a single connection to an EPP server.
// This implementation is not safe for concurrent use.
type Conn struct {
	net.Conn
	buf     bytes.Buffer
	decoder *xml.Decoder
	saved   xml.Decoder

	// Greeting holds the last received greeting message from the server,
	// indicating server name, status, data policy and capabilities.
	Greeting

	// LoginResult holds the last received login response message's Result
	// from the server, in which some servers might include diagnostics such
	// as connection count limits.
	LoginResult Result

	// Timeout defines the timeout for network operations.
	Timeout time.Duration
}

// NewConn initializes an epp.Conn from a net.Conn and performs the EPP
// handshake. It reads and stores the initial EPP <greeting> message.
// https://tools.ietf.org/html/rfc5730#section-2.4
func NewConn(conn net.Conn) (*Conn, error) {
	c := newConn(conn)
	g, err := c.readGreeting()
	if err == nil {
		c.Greeting = g
	}
	return c, err
}

// NewTimeoutConn initializes an epp.Conn like NewConn, limiting the duration of network
// operations on conn using Set(Read|Write)Deadline.
func NewTimeoutConn(conn net.Conn, timeout time.Duration) (*Conn, error) {
	c := newConn(conn)
	c.Timeout = timeout
	g, err := c.readGreeting()
	if err == nil {
		c.Greeting = g
	}
	return c, err
}

// Close sends an EPP <logout> command and closes the connection c.
func (c *Conn) Close() error {
	c.Logout()
	return c.Conn.Close()
}

// newConn initializes an epp.Conn from a net.Conn.
// Used internally for testing.
func newConn(conn net.Conn) *Conn {
	c := Conn{Conn: conn}
	c.decoder = xml.NewDecoder(&c.buf)
	c.saved = *c.decoder
	return &c
}

// reset resets the underlying xml.Decoder and bytes.Buffer,
// restoring the original state of the underlying
// xml.Decoder (pos 1, line 1, stack, etc.) using a hack.
func (c *Conn) reset() {
	c.buf.Reset()
	*c.decoder = c.saved // Heh.
}

// writeDataUnit writes a slice of bytes to c.
// Bytes written are prefixed with 32-bit header specifying the total size
// of the data unit (message + 4 byte header), in network (big-endian) order.
// http://www.ietf.org/rfc/rfc4934.txt
func (c *Conn) writeDataUnit(x []byte) error {
	logXML("<-- WRITE DATA UNIT -->", x)
	s := uint32(4 + len(x))
	if c.Timeout > 0 {
		c.Conn.SetWriteDeadline(time.Now().Add(c.Timeout))
	}
	err := binary.Write(c.Conn, binary.BigEndian, s)
	if err != nil {
		return err
	}
	_, err = c.Conn.Write(x)
	return err
}

// readResponse reads a single EPP response from c and parses the XML into req.
// It returns an error if the EPP response contains an error Result.
func (c *Conn) readResponse(res *Response) error {
	err := c.readDataUnit()
	if err != nil {
		return err
	}
	err = IgnoreEOF(scanResponse.Scan(c.decoder, res))
	if err != nil {
		return err
	}
	if res.Result.IsError() {
		return &res.Result
	}
	return nil
}

// readDataUnit reads a single EPP message from c into
// c.buf. The bytes in c.buf are valid until the next
// call to readDataUnit.
func (c *Conn) readDataUnit() error {
	c.reset()
	var s int32
	if c.Timeout > 0 {
		c.Conn.SetReadDeadline(time.Now().Add(c.Timeout))
	}
	err := binary.Read(c.Conn, binary.BigEndian, &s)
	if err != nil {
		return err
	}
	s -= 4 // https://tools.ietf.org/html/rfc5734#section-4
	if s < 0 {
		return io.ErrUnexpectedEOF
	}
	lr := io.LimitedReader{R: c.Conn, N: int64(s)}
	n, err := c.buf.ReadFrom(&lr)
	if err != nil {
		return err
	}
	if n != int64(s) || lr.N != 0 {
		return io.ErrUnexpectedEOF
	}
	logXML("<-- READ DATA UNIT -->", c.buf.Bytes())
	return nil
}

func deleteRange(s, pfx, sfx []byte) []byte {
	start := bytes.Index(s, pfx)
	if start < 0 {
		return s
	}
	end := bytes.Index(s[start+len(pfx):], sfx)
	if end < 0 {
		return s
	}
	end += start + len(pfx) + len(sfx)
	size := len(s) - (end - start)
	copy(s[start:size], s[end:])
	return s[:size]
}

func deleteBufferRange(buf *bytes.Buffer, pfx, sfx []byte) {
	v := deleteRange(buf.Bytes(), pfx, sfx)
	buf.Truncate(len(v))
}
<|endoftext|>


<commit_before>package mysqlclient

import (
	"errors"
	"net"

	"github.com/pubnative/mysqlproto-go"
)

type Conn struct {
	conn net.Conn
}

func NewConn(username, password, protocol, address, database string) (Conn, error) {
	conn, err := net.Dial(protocol, address)
	if err != nil {
		return Conn{}, err
	}

	if err = handshake(conn, username, password, database); err != nil {
		return Conn{}, err
	}

	return Conn{conn}, nil
}

func (c Conn) Close() error {
	return c.conn.Close()
}

func handshake(conn net.Conn, username, password, database string) error {
	packet, err := mysqlproto.ReadHandshakeV10(conn)
	if err != nil {
		return err
	}

	flags := packet.CapabilityFlags
	flags &= ^mysqlproto.CLIENT_SSL
	flags &= ^mysqlproto.CLIENT_COMPRESS

	res := mysqlproto.HandshakeResponse41(
		packet.CapabilityFlags&(flags),
		packet.CharacterSet,
		username,
		password,
		packet.AuthPluginData,
		database,
		packet.AuthPluginName,
		nil,
	)

	if _, err := conn.Write(res); err != nil {
		return err
	}

	streamPkt := mysqlproto.NewStreamPacket(conn)
	packetOK, err := streamPkt.NextPacket()
	if err != nil {
		return err
	}

	if packetOK.Payload[0] != mysqlproto.PACKET_OK {
		return errors.New("Error occured during handshake with a server")
	}

	return nil
}
<commit_msg>Set UTF-8 charset by default<commit_after>package mysqlclient

import (
	"errors"
	"net"

	"github.com/pubnative/mysqlproto-go"
)

type Conn struct {
	conn net.Conn
}

func NewConn(username, password, protocol, address, database string) (Conn, error) {
	conn, err := net.Dial(protocol, address)
	if err != nil {
		return Conn{}, err
	}

	if err = handshake(conn, username, password, database); err != nil {
		return Conn{}, err
	}

	if err = setUTF8Charset(conn); err != nil {
		return Conn{}, err
	}

	return Conn{conn}, nil
}

func (c Conn) Close() error {
	return c.conn.Close()
}

func handshake(conn net.Conn, username, password, database string) error {
	packet, err := mysqlproto.ReadHandshakeV10(conn)
	if err != nil {
		return err
	}

	flags := packet.CapabilityFlags
	flags &= ^mysqlproto.CLIENT_SSL
	flags &= ^mysqlproto.CLIENT_COMPRESS

	res := mysqlproto.HandshakeResponse41(
		packet.CapabilityFlags&(flags),
		packet.CharacterSet,
		username,
		password,
		packet.AuthPluginData,
		database,
		packet.AuthPluginName,
		nil,
	)

	if _, err := conn.Write(res); err != nil {
		return err
	}

	streamPkt := mysqlproto.NewStreamPacket(conn)
	packetOK, err := streamPkt.NextPacket()
	if err != nil {
		return err
	}

	if packetOK.Payload[0] != mysqlproto.PACKET_OK {
		return errors.New("Error occured during handshake with a server")
	}

	return nil
}

func setUTF8Charset(conn net.Conn) error {
	data := mysqlproto.ComQueryRequest([]byte("SET NAMES utf8"))
	if _, err := conn.Write(data); err != nil {
		return err
	}

	streamPkt := mysqlproto.NewStreamPacket(conn)
	packetOK, err := streamPkt.NextPacket()
	if err != nil {
		return err
	}

	if packetOK.Payload[0] != mysqlproto.PACKET_OK {
		return errors.New("Error occured during setting charset")
	}

	return nil
}
<|endoftext|>


<commit_before>package decimal

func (d D) Float64() float64 {
	f, _ := d.dec.Float64()
	return f
}
<commit_msg>add Int method<commit_after>package decimal

func (d D) Float64() float64 {
	f, _ := d.dec.Float64()
	return f
}

func (d D) Int() int {
	return int(d.dec.IntPart())
}
<|endoftext|>


<commit_before>/*
Package transl translates struct fields and store translations
in the same struct.
*/
package transl

import (
	"database/sql/driver"
	"encoding/json"
	"golang.org/x/net/context"
	"golang.org/x/text/language"
	"reflect"
	"sync"
)

var defaultLanguageString = "en"
var defaultLanguageTag = language.English

// SetDefaults redefines default language string and tag
func SetDefaults(str string, tag language.Tag) {
	defaultLanguageString = str
	defaultLanguageTag = tag
}

// StringTable is a type for struct field to hold translations
// e.g. Translations{"en": map[string]string{"name": "John"}}
type StringTable map[string]map[string]string

// Scan unmarshals translations from JSON
func (m *StringTable) Scan(value interface{}) error {
	return json.Unmarshal(value.([]byte), m)
}

// Value marshals translations to JSON
func (m StringTable) Value() (driver.Value, error) {
	b, err := json.Marshal(m)
	if err != nil {
		return nil, err
	}
	return string(b), nil
}

// Translate fills fields of `target` struct with translated values
//
func Translate(ctx context.Context, target interface{}) {
	meta := metas.getStructMeta(target)
	if !meta.valid {
		return
	}

	structValue := reflect.Indirect(reflect.ValueOf(target))

	translations, ok := structValue.FieldByName("Translations").Interface().(StringTable)
	if !ok || len(translations) == 0 {
		return
	}

	targetLanguages, ok := AcceptedLanguagesFromContext(ctx)
	if !ok || len(targetLanguages) == 0 {
		targetLanguages = []language.Tag{defaultLanguageTag}
	}

	for _, trF := range meta.fields {
		f := structValue.FieldByName(trF.name)
		if f.IsValid() && f.CanSet() && f.Kind() == reflect.String {
			translateField(f, trF.key, translations, targetLanguages)
		}
	}
}

func translateField(field reflect.Value, fieldName string, translations StringTable, targetLanguages []language.Tag) {
	matcher := getMatcher(fieldName, translations)
	effectiveLang, _, _ := matcher.Match(targetLanguages...)
	field.SetString(translations[effectiveLang.String()][fieldName])
}

var matchers = map[string]language.Matcher{}
var matchersMutex sync.RWMutex

func getMatcher(fieldName string, translations StringTable) language.Matcher {
	langs := []language.Tag{}
	defaultFound := false
	for lang, tr := range translations {
		_, ok := tr[fieldName]
		if ok {
			// First language in langs will be fallback option for matcher
			// but map order is not stable,
			// so we need to move default to front, if it's available
			if lang == defaultLanguageString {
				defaultFound = true
			} else {
				langs = append(langs, *getTagByString(lang))
			}
		}
	}
	if defaultFound {
		langs = append([]language.Tag{defaultLanguageTag}, langs...)
	}

	langsKey := ""
	for _, lang := range langs {
		langsKey += lang.String()
	}

	matchersMutex.RLock()
	matcher, ok := matchers[langsKey]
	matchersMutex.RUnlock()

	if ok {
		return matcher
	}

	matcher = language.NewMatcher(langs)

	matchersMutex.Lock()
	matchers[langsKey] = matcher
	matchersMutex.Unlock()

	return matcher
}

var tags = map[string]language.Tag{}
var tagsMutex sync.RWMutex

func getTagByString(s string) *language.Tag {
	tagsMutex.RLock()
	tag, ok := tags[s]
	tagsMutex.RUnlock()

	if ok {
		return &tag
	}

	tag = language.Make(s)

	tagsMutex.Lock()
	tags[s] = tag
	tagsMutex.Unlock()

	return &tag
}
<commit_msg>Remove two allocations with 10% speedup<commit_after>/*
Package transl translates struct fields and store translations
in the same struct.
*/
package transl

import (
	"database/sql/driver"
	"encoding/json"
	"golang.org/x/net/context"
	"golang.org/x/text/language"
	"reflect"
	"sync"
)

var defaultLanguageString = "en"
var defaultLanguageTag = language.English

// SetDefaults redefines default language string and tag
func SetDefaults(str string, tag language.Tag) {
	defaultLanguageString = str
	defaultLanguageTag = tag
}

// StringTable is a type for struct field to hold translations
// e.g. Translations{"en": map[string]string{"name": "John"}}
type StringTable map[string]map[string]string

// Scan unmarshals translations from JSON
func (m *StringTable) Scan(value interface{}) error {
	return json.Unmarshal(value.([]byte), m)
}

// Value marshals translations to JSON
func (m StringTable) Value() (driver.Value, error) {
	b, err := json.Marshal(m)
	if err != nil {
		return nil, err
	}
	return string(b), nil
}

// Translate fills fields of `target` struct with translated values
//
func Translate(ctx context.Context, target interface{}) {
	meta := metas.getStructMeta(target)
	if !meta.valid {
		return
	}

	structValue := reflect.Indirect(reflect.ValueOf(target))

	translations, ok := structValue.FieldByName("Translations").Interface().(StringTable)
	if !ok || len(translations) == 0 {
		return
	}

	targetLanguages, ok := AcceptedLanguagesFromContext(ctx)
	if !ok || len(targetLanguages) == 0 {
		targetLanguages = []language.Tag{defaultLanguageTag}
	}

	for _, trF := range meta.fields {
		f := structValue.FieldByName(trF.name)
		if f.IsValid() && f.CanSet() && f.Kind() == reflect.String {
			translateField(f, trF.key, translations, targetLanguages)
		}
	}
}

func translateField(field reflect.Value, fieldName string, translations StringTable, targetLanguages []language.Tag) {
	matcher := getMatcher(fieldName, translations)
	effectiveLang, _, _ := matcher.Match(targetLanguages...)
	field.SetString(translations[effectiveLang.String()][fieldName])
}

var matchers = map[string]language.Matcher{}
var matchersMutex sync.RWMutex

func getMatcher(fieldName string, translations StringTable) language.Matcher {
	var langs []language.Tag

	defaultFound := false
	v, ok := translations[defaultLanguageString]
	if ok {
		_, ok = v[fieldName]
		if ok {
			defaultFound = true
			langs = []language.Tag{defaultLanguageTag}
		}
	}
	if !defaultFound {
		langs = []language.Tag{}
	}

	for lang, tr := range translations {
		_, ok = tr[fieldName]
		if ok {
			// default language already in slice if needed
			if lang != defaultLanguageString {
				langs = append(langs, *getTagByString(lang))
			}
		}
	}

	langsKey := ""
	for _, lang := range langs {
		langsKey += lang.String()
	}

	matchersMutex.RLock()
	matcher, ok := matchers[langsKey]
	matchersMutex.RUnlock()

	if ok {
		return matcher
	}

	matcher = language.NewMatcher(langs)

	matchersMutex.Lock()
	matchers[langsKey] = matcher
	matchersMutex.Unlock()

	return matcher
}

var tags = map[string]language.Tag{}
var tagsMutex sync.RWMutex

func getTagByString(s string) *language.Tag {
	tagsMutex.RLock()
	tag, ok := tags[s]
	tagsMutex.RUnlock()

	if ok {
		return &tag
	}

	tag = language.Make(s)

	tagsMutex.Lock()
	tags[s] = tag
	tagsMutex.Unlock()

	return &tag
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/xml"
	"io/ioutil"
	"net/http"
)

var NicoAPIUrls = map[string]string{
	"login":        "https://secure.nicovideo.jp/secure/login?site=niconico",
	"getthumbinfo": "http://ext.nicovideo.jp/api/getthumbinfo/",
	"getflv":       "http://flapi.nicovideo.jp/api/getflv/",
}

type NicoVideoErrorInfo struct {
	XMLName     xml.Name `xml:"error"`
	Code        string   `xml:"code"`
	Description string   `xml:"description"`
}

type NicoVideoTag struct {
	XMLName xml.Name `xml:"tag"`
	Lock    string   `xml:"lock,attr"`
	Value   string   `xml:",innerxml"`
}

type NicoVideoTags struct {
	XMLName xml.Name       `xml:"tags"`
	Domain  string         `xml:"domain,attr"`
	Tag     []NicoVideoTag `xml:"tag"`
}

type NicoVideoInfo struct {
	XMLName       xml.Name        `xml:"thumb"`
	VideoId       string          `xml:"video_id"`
	Title         string          `xml:"title"`
	Description   string          `xml:"description"`
	ThumbnailUrl  string          `xml:"thumbnail_url"`
	FirstRetrieve string          `xml:"first_retrieve"`
	Length        string          `xml:"length"`
	MovieType     string          `xml:"movie_type"`
	SizeHigh      int             `xml:"size_high"`
	SizeLow       int             `xml:"size_low"`
	ViewCounter   int             `xml:"view_counter"`
	CommentNum    int             `xml:"comment_num"`
	MylistCounter int             `xml:"mylist_counter"`
	LastResBody   string          `xml:"last_res_body"`
	WatchUrl      string          `xml:"watch_url"`
	ThumbType     string          `xml:"thumb_type"`
	Embeddable    int             `xml:"embeddable"`
	NoLivePlay    int             `xml:"no_live_play"`
	Tags          []NicoVideoTags `xml:"tags"`
	UserId        int             `xml:"user_id"`
}

type NicoVideoThumbResponse struct {
	XMLName   xml.Name           `xml:"nicovideo_thumb_response"`
	Status    string             `xml:"status,attr"`
	ErrorInfo NicoVideoErrorInfo `xml:"error"`
	VideoInfo NicoVideoInfo      `xml:"thumb"`
}

func GetVideoThumbResponse(videoId string) (result NicoVideoThumbResponse, err error) {
	resp, _ := http.Get(NicoAPIUrls["getthumbinfo"] + videoId)
	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)
	err = xml.Unmarshal(body, &result)
	return
}

func GetVideoTags(videoId string) (returnTags []string) {
	resp, err := GetVideoThumbResponse(videoId)
	if err != nil {
		return
	}
	switch resp.Status {
	case "ok":
		videoInfo := resp.VideoInfo
		tags := videoInfo.Tags[0].Tag
		for _, tag := range tags {
			returnTags = append(returnTags, tag.Value)
		}
		return
	default:
		return
	}
}
<commit_msg>Refactor inner implementation of GetVideoTags function<commit_after>package main

import (
	"encoding/xml"
	"io/ioutil"
	"net/http"
)

var NicoAPIUrls = map[string]string{
	"login":        "https://secure.nicovideo.jp/secure/login?site=niconico",
	"getthumbinfo": "http://ext.nicovideo.jp/api/getthumbinfo/",
	"getflv":       "http://flapi.nicovideo.jp/api/getflv/",
}

type NicoVideoErrorInfo struct {
	XMLName     xml.Name `xml:"error"`
	Code        string   `xml:"code"`
	Description string   `xml:"description"`
}

type NicoVideoTag struct {
	XMLName xml.Name `xml:"tag"`
	Lock    string   `xml:"lock,attr"`
	Value   string   `xml:",innerxml"`
}

type NicoVideoTags struct {
	XMLName xml.Name       `xml:"tags"`
	Domain  string         `xml:"domain,attr"`
	Tag     []NicoVideoTag `xml:"tag"`
}

type NicoVideoInfo struct {
	XMLName       xml.Name        `xml:"thumb"`
	VideoId       string          `xml:"video_id"`
	Title         string          `xml:"title"`
	Description   string          `xml:"description"`
	ThumbnailUrl  string          `xml:"thumbnail_url"`
	FirstRetrieve string          `xml:"first_retrieve"`
	Length        string          `xml:"length"`
	MovieType     string          `xml:"movie_type"`
	SizeHigh      int             `xml:"size_high"`
	SizeLow       int             `xml:"size_low"`
	ViewCounter   int             `xml:"view_counter"`
	CommentNum    int             `xml:"comment_num"`
	MylistCounter int             `xml:"mylist_counter"`
	LastResBody   string          `xml:"last_res_body"`
	WatchUrl      string          `xml:"watch_url"`
	ThumbType     string          `xml:"thumb_type"`
	Embeddable    int             `xml:"embeddable"`
	NoLivePlay    int             `xml:"no_live_play"`
	Tags          []NicoVideoTags `xml:"tags"`
	UserId        int             `xml:"user_id"`
}

type NicoVideoThumbResponse struct {
	XMLName   xml.Name           `xml:"nicovideo_thumb_response"`
	Status    string             `xml:"status,attr"`
	ErrorInfo NicoVideoErrorInfo `xml:"error"`
	VideoInfo NicoVideoInfo      `xml:"thumb"`
}

func GetVideoThumbResponse(videoId string) (result NicoVideoThumbResponse, err error) {
	resp, _ := http.Get(NicoAPIUrls["getthumbinfo"] + videoId)
	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)
	err = xml.Unmarshal(body, &result)
	return
}

func GetVideoTags(videoId string) (tags []string) {
	resp, err := GetVideoThumbResponse(videoId)
	if err != nil {
		return
	}
	switch resp.Status {
	case "ok":
		videoInfo := resp.VideoInfo
		for _, tag := range videoInfo.Tags[0].Tag {
			tags = append(tags, tag.Value)
		}
		return
	default:
		return
	}
}
<|endoftext|>


<commit_before>/*
This code implements the flow chart that can be found here.
http://www.html5rocks.com/static/images/cors_server_flowchart.png

A Default Config for example is below:

	cors.Config{
		Origins:        "*",
		Methods:        "GET, PUT, POST, DELETE",
		RequestHeaders: "Origin, Authorization, Content-Type",
		ExposedHeaders: "",
		MaxAge: 1 * time.Minute,
		Credentials: true,
		ValidateHeaders: false,
	}
*/
package cors

import (
	"fmt"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
)

const (
	AllowOriginKey      string = "Access-Control-Allow-Origin"
	AllowCredentialsKey        = "Access-Control-Allow-Credentials"
	AllowHeadersKey            = "Access-Control-Allow-Headers"
	AllowMethodsKey            = "Access-Control-Allow-Methods"
	MaxAgeKey                  = "Access-Control-Max-Age"

	OriginKey         = "Origin"
	RequestMethodKey  = "Access-Control-Request-Method"
	RequestHeadersKey = "Access-Control-Request-Headers"
	ExposeHeadersKey  = "Access-Control-Expose-Headers"
)

const (
	optionsMethod = "OPTIONS"
)

/*
Config defines the configuration options available to control how the CORS middleware should function.
*/
type Config struct {
	// Enabling this causes us to compare Request-Method and Request-Headers to confirm they contain a subset of the Allowed Methods and Allowed Headers
	// The spec however allows for the server to always match, and simply return the allowed methods and headers. Either is supported in this middleware.
	ValidateHeaders bool

	// Comma delimited list of origin domains. Wildcard "*" is also allowed, and matches all origins.
	// If the origin does not match an item in the list, then the request is denied.
	Origins string
	origins []string

	// This are the headers that the resource supports, and will accept in the request.
	// Default is "Authorization".
	RequestHeaders string
	requestHeaders []string

	// These are headers that should be accessable by the CORS client, they are in addition to those defined by the spec as "simple response headers"
	//	 Cache-Control
	//	 Content-Language
	//	 Content-Type
	//	 Expires
	//	 Last-Modified
	//	 Pragma
	ExposedHeaders string

	// Comma delimited list of acceptable HTTP methods.
	Methods string
	methods []string

	// The amount of time in seconds that the client should cache the Preflight request
	MaxAge time.Duration
	maxAge string

	// If true, then cookies and Authorization headers are allowed along with the request.  This
	// is passed to the browser, but is not enforced.
	Credentials bool
	credentials string
}

// One time, do the conversion from our the public facing Configuration,
// to all the formats we use internally strings for headers.. slices for looping
func (config *Config) prepare() {
	config.origins = strings.Split(config.Origins, ", ")
	config.methods = strings.Split(config.Methods, ", ")
	config.requestHeaders = strings.Split(config.RequestHeaders, ", ")
	config.maxAge = fmt.Sprintf("%.f", config.MaxAge.Seconds())

	// Generates a boolean of value "true".
	config.credentials = fmt.Sprintf("%t", config.Credentials)

	// Convert to lower-case once as request headers are supposed to be a case-insensitive match
	for idx, header := range config.requestHeaders {
		config.requestHeaders[idx] = strings.ToLower(header)
	}
}

/*
Middleware generates a middleware handler function that works inside of a Gin request
to set the correct CORS headers.  It accepts a cors.Options struct for configuration.
*/
func Middleware(config Config) gin.HandlerFunc {
	forceOriginMatch := false

	if config.Origins == "" {
		panic("You must set at least a single valid origin. If you don't want CORS, to apply, simply remove the middleware.")
	}

	if config.Origins == "*" {
		forceOriginMatch = true
	}

	config.prepare()

	// Create the Middleware function
	return func(context *gin.Context) {
		// Read the Origin header from the HTTP request
		currentOrigin := context.Request.Header.Get(OriginKey)
		context.Writer.Header().Add("Vary", OriginKey)

		// CORS headers are added whenever the browser request includes an "Origin" header
		// However, if no Origin is supplied, they should never be added.
		if currentOrigin == "" {
			return
		}

		originMatch := false
		if !forceOriginMatch {
			originMatch = matchOrigin(currentOrigin, config)
		}

		if forceOriginMatch || originMatch {
			valid := false
			preflight := false

			if context.Request.Method == optionsMethod {
				requestMethod := context.Request.Header.Get(RequestMethodKey)
				if requestMethod != "" {
					preflight = true
					valid = handlePreflight(context, config, requestMethod)
				}
			}

			if !preflight {
				valid = handleRequest(context, config)
			}

			if valid {

				if config.Credentials {
					context.Writer.Header().Set(AllowCredentialsKey, config.credentials)
					// Allowed origins cannot be the string "*" cannot be used for a resource that supports credentials.
					context.Writer.Header().Set(AllowOriginKey, currentOrigin)
				} else if forceOriginMatch {
					context.Writer.Header().Set(AllowOriginKey, "*")
				} else {
					context.Writer.Header().Set(AllowOriginKey, currentOrigin)
				}

				//If this is a preflight request, we are finished, quit.
				//Otherwise this is a normal request and operations should proceed at normal
				if preflight {
					context.AbortWithStatus(200)
				}
				return
			}
		}

		//If it reaches here, it was not a valid request
		context.Abort()
	}
}

func handlePreflight(context *gin.Context, config Config, requestMethod string) bool {
	if ok := validateRequestMethod(requestMethod, config); ok == false {
		return false
	}

	if ok := validateRequestHeaders(context.Request.Header.Get(RequestHeadersKey), config); ok == true {
		context.Writer.Header().Set(AllowMethodsKey, config.Methods)
		context.Writer.Header().Set(AllowHeadersKey, config.RequestHeaders)

		if config.maxAge != "0" {
			context.Writer.Header().Set(MaxAgeKey, config.maxAge)
		}

		return true
	}

	return false
}

func handleRequest(context *gin.Context, config Config) bool {
	if config.ExposedHeaders != "" {
		context.Writer.Header().Set(ExposeHeadersKey, config.ExposedHeaders)
	}

	return true
}

// Case-sensitive match of origin header
func matchOrigin(origin string, config Config) bool {
	for _, value := range config.origins {
		if value == origin {
			return true
		}
	}
	return false
}

// Case-sensitive match of request method
func validateRequestMethod(requestMethod string, config Config) bool {
	if !config.ValidateHeaders {
		return true
	}

	if requestMethod != "" {
		for _, value := range config.methods {
			if value == requestMethod {
				return true
			}
		}
	}

	return false
}

// Case-insensitive match of request headers
func validateRequestHeaders(requestHeaders string, config Config) bool {
	if !config.ValidateHeaders {
		return true
	}

	headers := strings.Split(requestHeaders, ", ")

	for _, header := range headers {
		match := false
		header = strings.ToLower(header)

		for _, value := range config.requestHeaders {
			if value == header {
				match = true
				break
			}
		}

		if !match {
			return false
		}
	}

	return true
}
<commit_msg>ValidateRequestHeaders now not only accept ", " as separator<commit_after>/*
This code implements the flow chart that can be found here.
http://www.html5rocks.com/static/images/cors_server_flowchart.png

A Default Config for example is below:

	cors.Config{
		Origins:        "*",
		Methods:        "GET, PUT, POST, DELETE",
		RequestHeaders: "Origin, Authorization, Content-Type",
		ExposedHeaders: "",
		MaxAge: 1 * time.Minute,
		Credentials: true,
		ValidateHeaders: false,
	}
*/
package cors

import (
	"fmt"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
)

const (
	AllowOriginKey      string = "Access-Control-Allow-Origin"
	AllowCredentialsKey        = "Access-Control-Allow-Credentials"
	AllowHeadersKey            = "Access-Control-Allow-Headers"
	AllowMethodsKey            = "Access-Control-Allow-Methods"
	MaxAgeKey                  = "Access-Control-Max-Age"

	OriginKey         = "Origin"
	RequestMethodKey  = "Access-Control-Request-Method"
	RequestHeadersKey = "Access-Control-Request-Headers"
	ExposeHeadersKey  = "Access-Control-Expose-Headers"
)

const (
	optionsMethod = "OPTIONS"
)

/*
Config defines the configuration options available to control how the CORS middleware should function.
*/
type Config struct {
	// Enabling this causes us to compare Request-Method and Request-Headers to confirm they contain a subset of the Allowed Methods and Allowed Headers
	// The spec however allows for the server to always match, and simply return the allowed methods and headers. Either is supported in this middleware.
	ValidateHeaders bool

	// Comma delimited list of origin domains. Wildcard "*" is also allowed, and matches all origins.
	// If the origin does not match an item in the list, then the request is denied.
	Origins string
	origins []string

	// This are the headers that the resource supports, and will accept in the request.
	// Default is "Authorization".
	RequestHeaders string
	requestHeaders []string

	// These are headers that should be accessable by the CORS client, they are in addition to those defined by the spec as "simple response headers"
	//	 Cache-Control
	//	 Content-Language
	//	 Content-Type
	//	 Expires
	//	 Last-Modified
	//	 Pragma
	ExposedHeaders string

	// Comma delimited list of acceptable HTTP methods.
	Methods string
	methods []string

	// The amount of time in seconds that the client should cache the Preflight request
	MaxAge time.Duration
	maxAge string

	// If true, then cookies and Authorization headers are allowed along with the request.  This
	// is passed to the browser, but is not enforced.
	Credentials bool
	credentials string
}

// One time, do the conversion from our the public facing Configuration,
// to all the formats we use internally strings for headers.. slices for looping
func (config *Config) prepare() {
	config.origins = strings.Split(config.Origins, ", ")
	config.methods = strings.Split(config.Methods, ", ")
	config.requestHeaders = strings.Split(config.RequestHeaders, ", ")
	config.maxAge = fmt.Sprintf("%.f", config.MaxAge.Seconds())

	// Generates a boolean of value "true".
	config.credentials = fmt.Sprintf("%t", config.Credentials)

	// Convert to lower-case once as request headers are supposed to be a case-insensitive match
	for idx, header := range config.requestHeaders {
		config.requestHeaders[idx] = strings.ToLower(header)
	}
}

/*
Middleware generates a middleware handler function that works inside of a Gin request
to set the correct CORS headers.  It accepts a cors.Options struct for configuration.
*/
func Middleware(config Config) gin.HandlerFunc {
	forceOriginMatch := false

	if config.Origins == "" {
		panic("You must set at least a single valid origin. If you don't want CORS, to apply, simply remove the middleware.")
	}

	if config.Origins == "*" {
		forceOriginMatch = true
	}

	config.prepare()

	// Create the Middleware function
	return func(context *gin.Context) {
		// Read the Origin header from the HTTP request
		currentOrigin := context.Request.Header.Get(OriginKey)
		context.Writer.Header().Add("Vary", OriginKey)

		// CORS headers are added whenever the browser request includes an "Origin" header
		// However, if no Origin is supplied, they should never be added.
		if currentOrigin == "" {
			return
		}

		originMatch := false
		if !forceOriginMatch {
			originMatch = matchOrigin(currentOrigin, config)
		}

		if forceOriginMatch || originMatch {
			valid := false
			preflight := false

			if context.Request.Method == optionsMethod {
				requestMethod := context.Request.Header.Get(RequestMethodKey)
				if requestMethod != "" {
					preflight = true
					valid = handlePreflight(context, config, requestMethod)
				}
			}

			if !preflight {
				valid = handleRequest(context, config)
			}

			if valid {

				if config.Credentials {
					context.Writer.Header().Set(AllowCredentialsKey, config.credentials)
					// Allowed origins cannot be the string "*" cannot be used for a resource that supports credentials.
					context.Writer.Header().Set(AllowOriginKey, currentOrigin)
				} else if forceOriginMatch {
					context.Writer.Header().Set(AllowOriginKey, "*")
				} else {
					context.Writer.Header().Set(AllowOriginKey, currentOrigin)
				}

				//If this is a preflight request, we are finished, quit.
				//Otherwise this is a normal request and operations should proceed at normal
				if preflight {
					context.AbortWithStatus(200)
				}
				return
			}
		}

		//If it reaches here, it was not a valid request
		context.Abort()
	}
}

func handlePreflight(context *gin.Context, config Config, requestMethod string) bool {
	if ok := validateRequestMethod(requestMethod, config); ok == false {
		return false
	}

	if ok := validateRequestHeaders(context.Request.Header.Get(RequestHeadersKey), config); ok == true {
		context.Writer.Header().Set(AllowMethodsKey, config.Methods)
		context.Writer.Header().Set(AllowHeadersKey, config.RequestHeaders)

		if config.maxAge != "0" {
			context.Writer.Header().Set(MaxAgeKey, config.maxAge)
		}

		return true
	}

	return false
}

func handleRequest(context *gin.Context, config Config) bool {
	if config.ExposedHeaders != "" {
		context.Writer.Header().Set(ExposeHeadersKey, config.ExposedHeaders)
	}

	return true
}

// Case-sensitive match of origin header
func matchOrigin(origin string, config Config) bool {
	for _, value := range config.origins {
		if value == origin {
			return true
		}
	}
	return false
}

// Case-sensitive match of request method
func validateRequestMethod(requestMethod string, config Config) bool {
	if !config.ValidateHeaders {
		return true
	}

	if requestMethod != "" {
		for _, value := range config.methods {
			if value == requestMethod {
				return true
			}
		}
	}

	return false
}

// Case-insensitive match of request headers
func validateRequestHeaders(requestHeaders string, config Config) bool {
	if !config.ValidateHeaders {
		return true
	}

	headers := strings.Split(requestHeaders, ", ")

	for _, header := range headers {
		match := false
		header = strings.ToLower(strings.Trim(header, " \t\r\n"))

		for _, value := range config.requestHeaders {
			if value == header {
				match = true
				break
			}
		}

		if !match {
			return false
		}
	}

	return true
}
<|endoftext|>