<commit_before>package main

import "fmt"
import "os"
import "unsafe"

type PrintCyclicer interface {
	PrintCyclic(prefix string, seen map[unsafe.Pointer]bool)
}

func genNode(format string, objs ...interface{}) {
	fmt.Printf("%s%p [label=\"" + format + "\"]\n", objs...)
}

func genLink(seen map[unsafe.Pointer]bool, format string, objs ...interface{}) {
	fmt.Printf("%s%p -> %[1]s%[3]p [label=\"" + format + "\"]\n", objs...)
	objs[2].(PrintCyclicer).PrintCyclic(objs[0].(string), seen)
}

// immutable
type TaggedTreeList struct {
	left, right *TaggedTreeList
	size, termCount int
	leaf *TermNode
}

func NewTaggedTreeList(term *TermNode) *TaggedTreeList {
	t := 0
	if term.varNode == nil {
		t = 1
	}
	return &TaggedTreeList{
		nil,
		nil,
		1,
		t,
		term,
	}
}

func (l *TaggedTreeList) Append(term *TermNode) *TaggedTreeList {
	return l.Concat(NewTaggedTreeList(term))
}

func (l *TaggedTreeList) Concat(r *TaggedTreeList) *TaggedTreeList {
	return &TaggedTreeList{
		l,
		r,
		l.size + r.size,
		l.termCount + r.termCount,
		nil,
	}
}

func (l *TaggedTreeList) Slice() []*TermNode {
	s := make([]*TermNode, 0, l.size)
	return l.slice(s)
}

func (l *TaggedTreeList) slice(s []*TermNode) []*TermNode {
	if l.leaf == nil {
		s = l.left.slice(s)
		s = l.right.slice(s)
	} else {
		s = append(s, l.leaf)
	}
	return s
}

func (l *TaggedTreeList) TermCount() int {
	if l == nil {
		return 0
	}
	return l.termCount
}

func (l *TaggedTreeList) PrintCyclicRoot(prefix string) {
	fmt.Println("subgraph {")
	l.PrintCyclic(prefix, make(map[unsafe.Pointer]bool))
	fmt.Println("}")
}

func (l *TaggedTreeList) PrintCyclic(prefix string, seen map[unsafe.Pointer]bool) {
	ptr := unsafe.Pointer(l)
	if !seen[ptr] {
		seen[ptr] = true
		genNode("%d/%d", prefix, l, l.termCount, l.size)
		if l.leaf != nil {
			genLink(seen, "leaf", prefix, l, l.leaf)
		} else {
			genLink(seen, "left", prefix, l, l.left)
			genLink(seen, "right", prefix, l, l.right)
		}
	}
}

// immutable
type Symbol struct {
	name string
	arity int
}

func (s *Symbol) PrintCyclic(prefix string, seen map[unsafe.Pointer]bool) {
	ptr := unsafe.Pointer(s)
	if !seen[ptr] {
		seen[ptr] = true
		genNode("%s/%d", prefix, s, s.name, s.arity)
	}
}

type TermNode struct {
	symbol *Symbol // immutable
	varNode *VarNode // const
	child []*TermNode // const (slice object only)
	done bool // mutable
}

func (t *TermNode) Substitute() {
	if !t.done {
		t.done = true
		for i, c := range t.child {
			if c.varNode == nil {
				c.Substitute()
			} else {
				ts := rep(c.varNode).terms.Slice()
				if len(ts) != 1 {
					panic("Attempted to substitute variable with multiple terms")
				}
				t.child[i] = ts[0]
			}
		}
	}
}

func (t *TermNode) PrintCyclic(prefix string, seen map[unsafe.Pointer]bool) {
	ptr := unsafe.Pointer(t)
	if !seen[ptr] {
		seen[ptr] = true
		suffix := ""
		if t.done {
			suffix = " ✓"
		}
		if t.symbol != nil {
			genNode("%s%s", prefix, t, t.symbol.name, suffix)
		} else if t.varNode != nil {
			genNode("%s%s", prefix, t, t.varNode.symbol, suffix)
			genLink(seen, "var", prefix, t, t.varNode)
		}
		for i, c := range t.child {
			genLink(seen, "#%d", prefix, t, c, i)
		}
	}
}

type VarNode struct {
	symbol string // immutable
	rep *VarNode // mutable
	terms *TaggedTreeList // mutable
	varCount int // mutable
}

func (v *VarNode) PrintCyclic(prefix string, seen map[unsafe.Pointer]bool) {
	ptr := unsafe.Pointer(v)
	if !seen[ptr] {
		seen[ptr] = true
		genNode("%s (%d)", prefix, v, v.symbol, v.varCount)
		if v.rep != nil {
			genLink(seen, "rep", prefix, v, v.rep)
		}
		if v.terms != nil {
			genLink(seen, "terms", prefix, v, v.terms)
		}
	}
}

type UnificationError struct {
	left, right *Symbol
}

var queue []*VarNode

func add(v *VarNode, t *TermNode) {
	if v.terms.TermCount() == 1 {
		queue = append(queue, v)
	}
	if v.terms == nil {
		v.terms = NewTaggedTreeList(t)
	} else {
		v.terms = v.terms.Append(t)
	}
}

func merge(v1, v2 *VarNode) {
	r1, r2 := v1.varCount, v2.varCount
	var bigV, v *VarNode
	if r1 >= r2 {
		bigV, v = v1, v2
	} else {
		bigV, v = v2, v1
	}
	k1, k2 := bigV.terms.TermCount(), v.terms.TermCount()
	if k1 <= 1 && k1 + k2 > 1 {
		queue = append(queue, bigV)
	}
	if bigV.terms == nil {
		bigV.terms = v.terms
	} else if v.terms != nil {
		bigV.terms = bigV.terms.Concat(v.terms)
	}
	v.rep, v.terms, v.varCount = bigV, nil, 0
	bigV.varCount = r1 + r1
}

func rep(v *VarNode) *VarNode {
	v0 := v.rep
	for v0 != v0.rep {
		v0 = v0.rep
	}
	for v.rep != v0 {
		v.rep, v = v0, v.rep
	}
	return v0
}

func commonFrontier(index int, parents, t_list []*TermNode) *UnificationError {
	sym := t_list[0].symbol
	for _, term := range t_list {
		if term.symbol != sym {
			return &UnificationError{
				left:  sym,
				right: term.symbol,
			}
		}
	}
	a := sym.arity
	t0_list := make([]*TermNode, len(t_list))
	for i := 0; i < a; i++ {
		for j := range t_list {
			t0_list[j] = t_list[j].child[i]
		}
		j := -1
		for k, term := range t0_list {
			if term.varNode != nil {
				j = k
				break
			}
		}
		if j != -1 {
			// In the original this unconditionally swaps them in memory
			if parents != nil {
				tmp := parents[0].child[index]
				parents[0].child[index] = parents[j].child[index]
				parents[j].child[index] = tmp
			}
			v := rep(t0_list[j].varNode)
			for k, term := range t0_list {
				if k == j || term.varNode == nil {
					continue
				}
				v2 := rep(term.varNode)
				if v != v2 {
					merge(v, v2)
				}
			}
			for _, term := range t0_list {
				if term.varNode != nil {
					continue
				}
				add(v, term)
			}
		} else {
			return commonFrontier(i, t_list, t0_list)
		}
	}
	return nil
}

func unify(t_list []*TermNode) *UnificationError {
	queue = make([]*VarNode, 0)
	err := commonFrontier(0, nil, t_list)
	if err != nil {
		return err
	}
	for len(queue) != 0 {
		v := queue[0]
		queue = queue[1:]
		k := v.terms.TermCount()
		if k >= 2 {
			t := v.terms.Slice()
			v.terms = NewTaggedTreeList(t[0])
			err = commonFrontier(0, nil, t)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

func main() {
	sym := func(name string, arity int) *Symbol {
		return &Symbol{ name, arity }
	}
	varNode := func(name string) *VarNode {
		v := &VarNode{ name, nil, nil, 1 }
		v.rep = v
		return v
	}
	varTerm := func(node *VarNode) *TermNode {
		return &TermNode{ nil, node, nil, false }
	}
	funcTerm := func(symbol *Symbol, children ...*TermNode) *TermNode {
		return &TermNode{ symbol, nil, children, false }
	}
	f := sym("f", 2)
	x := varTerm(varNode("x"))
	z := varTerm(varNode("z"))
	expr1 := funcTerm(f, x, x)
	expr2 := funcTerm(f, funcTerm(f, z, z), funcTerm(f, z, x))
	root := NewTaggedTreeList(expr1).Append(expr2)
	fmt.Println("digraph {")
	root.PrintCyclicRoot("initial")
	err := unify(root.Slice())
	if err != nil {
		fmt.Printf("Error: %+v\n", err)
		os.Exit(1)
	}
	root.PrintCyclicRoot("unify")
	expr1.Substitute()
	expr2.Substitute()
	root.PrintCyclicRoot("substitute")
	fmt.Println("}")
}
<commit_msg>Localise global queue<commit_after>package main

import "fmt"
import "os"
import "unsafe"

type PrintCyclicer interface {
	PrintCyclic(prefix string, seen map[unsafe.Pointer]bool)
}

func genNode(format string, objs ...interface{}) {
	fmt.Printf("%s%p [label=\"" + format + "\"]\n", objs...)
}

func genLink(seen map[unsafe.Pointer]bool, format string, objs ...interface{}) {
	fmt.Printf("%s%p -> %[1]s%[3]p [label=\"" + format + "\"]\n", objs...)
	objs[2].(PrintCyclicer).PrintCyclic(objs[0].(string), seen)
}

// immutable
type TaggedTreeList struct {
	left, right *TaggedTreeList
	size, termCount int
	leaf *TermNode
}

func NewTaggedTreeList(term *TermNode) *TaggedTreeList {
	t := 0
	if term.varNode == nil {
		t = 1
	}
	return &TaggedTreeList{
		nil,
		nil,
		1,
		t,
		term,
	}
}

func (l *TaggedTreeList) Append(term *TermNode) *TaggedTreeList {
	return l.Concat(NewTaggedTreeList(term))
}

func (l *TaggedTreeList) Concat(r *TaggedTreeList) *TaggedTreeList {
	return &TaggedTreeList{
		l,
		r,
		l.size + r.size,
		l.termCount + r.termCount,
		nil,
	}
}

func (l *TaggedTreeList) Slice() []*TermNode {
	s := make([]*TermNode, 0, l.size)
	return l.slice(s)
}

func (l *TaggedTreeList) slice(s []*TermNode) []*TermNode {
	if l.leaf == nil {
		s = l.left.slice(s)
		s = l.right.slice(s)
	} else {
		s = append(s, l.leaf)
	}
	return s
}

func (l *TaggedTreeList) TermCount() int {
	if l == nil {
		return 0
	}
	return l.termCount
}

func (l *TaggedTreeList) PrintCyclicRoot(prefix string) {
	fmt.Println("subgraph {")
	l.PrintCyclic(prefix, make(map[unsafe.Pointer]bool))
	fmt.Println("}")
}

func (l *TaggedTreeList) PrintCyclic(prefix string, seen map[unsafe.Pointer]bool) {
	ptr := unsafe.Pointer(l)
	if !seen[ptr] {
		seen[ptr] = true
		genNode("%d/%d", prefix, l, l.termCount, l.size)
		if l.leaf != nil {
			genLink(seen, "leaf", prefix, l, l.leaf)
		} else {
			genLink(seen, "left", prefix, l, l.left)
			genLink(seen, "right", prefix, l, l.right)
		}
	}
}

// immutable
type Symbol struct {
	name string
	arity int
}

func (s *Symbol) PrintCyclic(prefix string, seen map[unsafe.Pointer]bool) {
	ptr := unsafe.Pointer(s)
	if !seen[ptr] {
		seen[ptr] = true
		genNode("%s/%d", prefix, s, s.name, s.arity)
	}
}

type TermNode struct {
	symbol *Symbol // immutable
	varNode *VarNode // const
	child []*TermNode // const (slice object only)
	done bool // mutable
}

func (t *TermNode) Substitute() {
	if !t.done {
		t.done = true
		for i, c := range t.child {
			if c.varNode == nil {
				c.Substitute()
			} else {
				ts := rep(c.varNode).terms.Slice()
				if len(ts) != 1 {
					panic("Attempted to substitute variable with multiple terms")
				}
				t.child[i] = ts[0]
			}
		}
	}
}

func (t *TermNode) PrintCyclic(prefix string, seen map[unsafe.Pointer]bool) {
	ptr := unsafe.Pointer(t)
	if !seen[ptr] {
		seen[ptr] = true
		suffix := ""
		if t.done {
			suffix = " ✓"
		}
		if t.symbol != nil {
			genNode("%s%s", prefix, t, t.symbol.name, suffix)
		} else if t.varNode != nil {
			genNode("%s%s", prefix, t, t.varNode.symbol, suffix)
			genLink(seen, "var", prefix, t, t.varNode)
		}
		for i, c := range t.child {
			genLink(seen, "#%d", prefix, t, c, i)
		}
	}
}

type VarNode struct {
	symbol string // immutable
	rep *VarNode // mutable
	terms *TaggedTreeList // mutable
	varCount int // mutable
}

func (v *VarNode) PrintCyclic(prefix string, seen map[unsafe.Pointer]bool) {
	ptr := unsafe.Pointer(v)
	if !seen[ptr] {
		seen[ptr] = true
		genNode("%s (%d)", prefix, v, v.symbol, v.varCount)
		if v.rep != nil {
			genLink(seen, "rep", prefix, v, v.rep)
		}
		if v.terms != nil {
			genLink(seen, "terms", prefix, v, v.terms)
		}
	}
}

type UnificationError struct {
	left, right *Symbol
}

func add(queue []*VarNode, v *VarNode, t *TermNode) []*VarNode {
	if v.terms == nil {
		v.terms = NewTaggedTreeList(t)
	} else {
		v.terms = v.terms.Append(t)
	}
	if v.terms.TermCount() == 2 {
		return append(queue, v)
	}
	return queue
}

func merge(queue []*VarNode, v1, v2 *VarNode) []*VarNode {
	r1, r2 := v1.varCount, v2.varCount
	var bigV, v *VarNode
	if r1 >= r2 {
		bigV, v = v1, v2
	} else {
		bigV, v = v2, v1
	}
	small := bigV.terms.TermCount() <= 1
	if bigV.terms == nil {
		bigV.terms = v.terms
	} else if v.terms != nil {
		bigV.terms = bigV.terms.Concat(v.terms)
	}
	v.rep, v.terms, v.varCount = bigV, nil, 0
	bigV.varCount = r1 + r1
	if small && bigV.terms.TermCount() >= 2 {
		return append(queue, bigV)
	}
	return queue
}

func rep(v *VarNode) *VarNode {
	v0 := v.rep
	for v0 != v0.rep {
		v0 = v0.rep
	}
	for v.rep != v0 {
		v.rep, v = v0, v.rep
	}
	return v0
}

func commonFrontier(queue []*VarNode, index int, parents, t_list []*TermNode) ([]*VarNode, *UnificationError) {
	sym := t_list[0].symbol
	for _, term := range t_list {
		if term.symbol != sym {
			return nil, &UnificationError{
				left:  sym,
				right: term.symbol,
			}
		}
	}
	a := sym.arity
	t0_list := make([]*TermNode, len(t_list))
	for i := 0; i < a; i++ {
		for j := range t_list {
			t0_list[j] = t_list[j].child[i]
		}
		j := -1
		for k, term := range t0_list {
			if term.varNode != nil {
				j = k
				break
			}
		}
		if j != -1 {
			// In the original this unconditionally swaps them in memory
			if parents != nil {
				tmp := parents[0].child[index]
				parents[0].child[index] = parents[j].child[index]
				parents[j].child[index] = tmp
			}
			v := rep(t0_list[j].varNode)
			for k, term := range t0_list {
				if k == j || term.varNode == nil {
					continue
				}
				v2 := rep(term.varNode)
				if v != v2 {
					queue = merge(queue, v, v2)
				}
			}
			for _, term := range t0_list {
				if term.varNode != nil {
					continue
				}
				queue = add(queue, v, term)
			}
		} else {
			return commonFrontier(queue, i, t_list, t0_list)
		}
	}
	return queue, nil
}

func unify(t_list []*TermNode) *UnificationError {
	queue := make([]*VarNode, 0)
	queue, err := commonFrontier(queue, 0, nil, t_list)
	if err != nil {
		return err
	}
	for len(queue) != 0 {
		v := queue[0]
		queue = queue[1:]
		k := v.terms.TermCount()
		if k >= 2 {
			t := v.terms.Slice()
			v.terms = NewTaggedTreeList(t[0])
			queue, err = commonFrontier(queue, 0, nil, t)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

func main() {
	sym := func(name string, arity int) *Symbol {
		return &Symbol{ name, arity }
	}
	varNode := func(name string) *VarNode {
		v := &VarNode{ name, nil, nil, 1 }
		v.rep = v
		return v
	}
	varTerm := func(node *VarNode) *TermNode {
		return &TermNode{ nil, node, nil, false }
	}
	funcTerm := func(symbol *Symbol, children ...*TermNode) *TermNode {
		return &TermNode{ symbol, nil, children, false }
	}
	f := sym("f", 2)
	x := varTerm(varNode("x"))
	z := varTerm(varNode("z"))
	expr1 := funcTerm(f, x, x)
	expr2 := funcTerm(f, funcTerm(f, z, z), funcTerm(f, z, x))
	root := NewTaggedTreeList(expr1).Append(expr2)
	fmt.Println("digraph {")
	root.PrintCyclicRoot("initial")
	err := unify(root.Slice())
	if err != nil {
		fmt.Printf("Error: %+v\n", err)
		os.Exit(1)
	}
	root.PrintCyclicRoot("unify")
	expr1.Substitute()
	expr2.Substitute()
	root.PrintCyclicRoot("substitute")
	fmt.Println("}")
}
<|endoftext|>


<commit_before>package textmagic

import (
	"encoding/json"
	"io"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/MJKWoolnough/memio"
)

var apiURLPrefix = "https://www.textmagic.com/app/api?"

const (
	cmdAccount       = "account"
	cmdCheckNumber   = "check_number"
	cmdDeleteReply   = "delete_reply"
	cmdMessageStatus = "message_status"
	cmdReceive       = "receive"
	cmdSend          = "send"
)

// TextMagic contains the data necessary for performing API requests
type TextMagic struct {
	username, password string
}

// New constructs a new TextMagic session
func New(username, password string) TextMagic {
	return TextMagic{username, password}
}

func (t TextMagic) sendAPI(cmd string, params url.Values, data interface{}) error {
	params.Set("username", t.username)
	params.Set("password", t.password)
	params.Set("cmd", cmd)
	r, err := http.Get(apiURLPrefix + params.Encode())
	if err != nil {
		return RequestError{cmd, err}
	}
	defer r.Body.Close()
	if r.StatusCode != http.StatusOK {
		return StatusError{cmd, r.StatusCode}
	}
	cL := r.ContentLength
	if cL < 0 {
		cL = 1024
	}
	jsonData := make([]byte, 0, cL) // avoid allocation using io.Pipe?
	var apiError APIError
	err = json.NewDecoder(io.TeeReader(r.Body, memio.Create(&jsonData))).Decode(&apiError)
	if err != nil {
		return JSONError{cmd, err}
	}
	if apiError.Code != 0 {
		apiError.Cmd = cmd
		return apiError
	}
	json.Unmarshal(jsonData, data)
	return nil
}

type balance struct {
	Balance float32 `json:"balance"`
}

// Account returns the balance of the given TextMagic account
func (t TextMagic) Account() (float32, error) {
	var b balance
	if err := t.sendAPI(cmdAccount, url.Values{}, &b); err != nil {
		return 0, err
	}
	return b.Balance, nil
}

// DeliveryNotificationCode is a representation of the status of a delivery
type DeliveryNotificationCode string

// Status returns the type of status based on the code
func (d DeliveryNotificationCode) Status() string {
	switch d {
	case "q", "r", "a", "b", "s":
		return "intermediate"
	case "d", "f", "e", "j", "u":
		return "final"
	}
	return "unknown"
}

func (d DeliveryNotificationCode) String() string {
	switch d {
	case "q":
		return "The message is queued on the TextMagic server."
	case "r":
		return "The message has been sent to the mobile operator."
	case "a":
		return "The mobile operator has acknowledged the message."
	case "b":
		return "The mobile operator has queued the message."
	case "d":
		return "The message has been successfully delivered to the handset."
	case "f":
		return "An error occurred while delivering message."
	case "e":
		return "An error occurred while sending message."
	case "j":
		return "The mobile operator has rejected the message."
	case "s":
		return "This message is scheduled to be sent later."
	default:
		return "The status is unknown."

	}
}

// Status represents all of the information about a sent/pending message
type Status struct {
	Text      string                   `json:"text"`
	Status    DeliveryNotificationCode `json:"status"`
	Created   int64                    `json:"created_time"`
	Reply     string                   `json:"reply_number"`
	Cost      float32                  `json:"credits_cost"`
	Completed int64                    `json:"completed_time"`
}

const joinSep = ","

// MessageStatus gathers information about the messages with the given ids
func (t TextMagic) MessageStatus(ids []string) (map[string]Status, error) {
	statuses := make(map[string]Status)
	for _, tIds := range splitSlice(ids) {
		messageIds := strings.Join(tIds, joinSep)
		strStatuses := make(map[string]Status)
		err := t.sendAPI(cmdMessageStatus, url.Values{"ids": {messageIds}}, strStatuses)
		if err != nil {
			return statuses, err
		}
		for messageID, status := range strStatuses {
			statuses[messageID] = status
		}
	}
	return statuses, nil
}

// Number represents the information about a phone number
type Number struct {
	Price   float32 `json:"price"`
	Country string  `json:"country"`
}

// CheckNumber is used to get the cost and country for the given phone numbers
func (t TextMagic) CheckNumber(numbers []string) (map[string]Number, error) {
	ns := make(map[string]Number)
	if err := t.sendAPI(cmdCheckNumber, url.Values{"phone": {strings.Join(numbers, joinSep)}}, ns); err != nil {
		return nil, err
	}
	return ns, nil
}

type deleted struct {
	Deleted []string `json:"deleted"`
}

// DeleteReply will simple delete message replies with the given ids
func (t TextMagic) DeleteReply(ids []string) ([]string, error) {
	toRet := make([]string, 0, len(ids))
	for _, tIds := range splitSlice(ids) {
		var d deleted
		if err := t.sendAPI(cmdDeleteReply, url.Values{"deleted": {strings.Join(tIds, joinSep)}}, &d); err != nil {
			return toRet, err
		}
		toRet = append(toRet, d.Deleted...)
	}
	return toRet, nil
}

// Message represents the information about a received message
type Message struct {
	ID        uint64 `json:"message_id"`
	From      string `json:"from"`
	Timestamp int64  `json:"timestamp"`
	Text      string `json:"text"`
}

type received struct {
	Messages []Message `json:"messages"`
	Unread   uint64    `json:"unread"`
}

// Receive will retrieve the number of unread messages and the 100 latest
// replies
func (t TextMagic) Receive(lastRetrieved uint64) (uint64, []Message, error) {
	var r received
	err := t.sendAPI(cmdReceive, url.Values{"last_retrieved_id": {utos(lastRetrieved)}}, &r)
	return r.Unread, r.Messages, err
}

// Option is a type representing a message sending option
type Option func(u url.Values)

// From is an option to modify the sender of a message
func From(from string) Option {
	return func(u url.Values) {
		u.Set("from", from)
	}
}

// MaxLength is an option to limit the length of a message
func MaxLength(length uint64) Option {
	if length > 3 {
		length = 3
	}
	return func(u url.Values) {
		u.Set("max_length", utos(length))
	}
}

// CutExtra sets the option to automatically trim overlong messages
func CutExtra() Option {
	return func(u url.Values) {
		u.Set("cut_extra", "1")
	}
}

// SendTime sets the option to schedule the sending of a message for a specific
// time
func SendTime(t time.Time) Option {
	return func(u url.Values) {
		u.Set("send_time", t.Format(time.RFC3339))
	}
}

type messageResponse struct {
	IDs   map[string]string `json:"message_id"`
	Text  string            `json:"sent_text"`
	Parts uint              `json:"parts_count"`
}

// Send will send a message to the given recipients. It takes options to modify
// the scheduling. sender and length of the message
func (t TextMagic) Send(message string, to []string, options ...Option) (map[string]string, string, uint, error) {
	var (
		params = url.Values{}
		text   string
		parts  uint
		ids    = make(map[string]string)
	)
	// check message for unicode/invalid chars
	params.Set("text", message)
	params.Set("unicode", "0")
	for _, o := range options {
		o(params)
	}
	for _, numbers := range splitSlice(to) {
		params.Set("phone", strings.Join(numbers, joinSep))
		var m messageResponse
		if err := t.sendAPI(cmdSend, params, &m); err != nil {
			return ids, text, parts, err
		}
		if parts == 0 {
			parts = m.Parts
			text = m.Text
		}
		for id, number := range m.IDs {
			ids[id] = number
		}
	}
	return ids, text, parts, nil
}

// Errors

// APIError is an error returned when the incorrect or unexpected data is
// received
type APIError struct {
	Cmd     string
	Code    int    `json:"error_code"`
	Message string `json:"error_message"`
}

func (a APIError) Error() string {
	return "command " + a.Cmd + " returned the following API error: " + a.Message
}

// RequestError is an error which wraps an error that occurs while making an
// API call
type RequestError struct {
	Cmd string
	Err error
}

func (r RequestError) Error() string {
	return "command " + r.Cmd + " returned the following error while makeing the API call: " + r.Err.Error()
}

// StatusError is an error that is returned when a non-200 OK http response is
// received
type StatusError struct {
	Cmd        string
	StatusCode int
}

func (s StatusError) Error() string {
	return "command " + s.Cmd + " returned a non-200 OK response: " + http.StatusText(s.StatusCode)
}

// JSONError is an error that wraps a JSON error
type JSONError struct {
	Cmd string
	Err error
}

func (j JSONError) Error() string {
	return "command " + j.Cmd + " returned malformed JSON: " + j.Err.Error()
}
<commit_msg>Added package comment<commit_after>// Package textmagic wraps the API for textmagic.com
package textmagic

import (
	"encoding/json"
	"io"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/MJKWoolnough/memio"
)

var apiURLPrefix = "https://www.textmagic.com/app/api?"

const (
	cmdAccount       = "account"
	cmdCheckNumber   = "check_number"
	cmdDeleteReply   = "delete_reply"
	cmdMessageStatus = "message_status"
	cmdReceive       = "receive"
	cmdSend          = "send"
)

// TextMagic contains the data necessary for performing API requests
type TextMagic struct {
	username, password string
}

// New constructs a new TextMagic session
func New(username, password string) TextMagic {
	return TextMagic{username, password}
}

func (t TextMagic) sendAPI(cmd string, params url.Values, data interface{}) error {
	params.Set("username", t.username)
	params.Set("password", t.password)
	params.Set("cmd", cmd)
	r, err := http.Get(apiURLPrefix + params.Encode())
	if err != nil {
		return RequestError{cmd, err}
	}
	defer r.Body.Close()
	if r.StatusCode != http.StatusOK {
		return StatusError{cmd, r.StatusCode}
	}
	cL := r.ContentLength
	if cL < 0 {
		cL = 1024
	}
	jsonData := make([]byte, 0, cL) // avoid allocation using io.Pipe?
	var apiError APIError
	err = json.NewDecoder(io.TeeReader(r.Body, memio.Create(&jsonData))).Decode(&apiError)
	if err != nil {
		return JSONError{cmd, err}
	}
	if apiError.Code != 0 {
		apiError.Cmd = cmd
		return apiError
	}
	json.Unmarshal(jsonData, data)
	return nil
}

type balance struct {
	Balance float32 `json:"balance"`
}

// Account returns the balance of the given TextMagic account
func (t TextMagic) Account() (float32, error) {
	var b balance
	if err := t.sendAPI(cmdAccount, url.Values{}, &b); err != nil {
		return 0, err
	}
	return b.Balance, nil
}

// DeliveryNotificationCode is a representation of the status of a delivery
type DeliveryNotificationCode string

// Status returns the type of status based on the code
func (d DeliveryNotificationCode) Status() string {
	switch d {
	case "q", "r", "a", "b", "s":
		return "intermediate"
	case "d", "f", "e", "j", "u":
		return "final"
	}
	return "unknown"
}

func (d DeliveryNotificationCode) String() string {
	switch d {
	case "q":
		return "The message is queued on the TextMagic server."
	case "r":
		return "The message has been sent to the mobile operator."
	case "a":
		return "The mobile operator has acknowledged the message."
	case "b":
		return "The mobile operator has queued the message."
	case "d":
		return "The message has been successfully delivered to the handset."
	case "f":
		return "An error occurred while delivering message."
	case "e":
		return "An error occurred while sending message."
	case "j":
		return "The mobile operator has rejected the message."
	case "s":
		return "This message is scheduled to be sent later."
	default:
		return "The status is unknown."

	}
}

// Status represents all of the information about a sent/pending message
type Status struct {
	Text      string                   `json:"text"`
	Status    DeliveryNotificationCode `json:"status"`
	Created   int64                    `json:"created_time"`
	Reply     string                   `json:"reply_number"`
	Cost      float32                  `json:"credits_cost"`
	Completed int64                    `json:"completed_time"`
}

const joinSep = ","

// MessageStatus gathers information about the messages with the given ids
func (t TextMagic) MessageStatus(ids []string) (map[string]Status, error) {
	statuses := make(map[string]Status)
	for _, tIds := range splitSlice(ids) {
		messageIds := strings.Join(tIds, joinSep)
		strStatuses := make(map[string]Status)
		err := t.sendAPI(cmdMessageStatus, url.Values{"ids": {messageIds}}, strStatuses)
		if err != nil {
			return statuses, err
		}
		for messageID, status := range strStatuses {
			statuses[messageID] = status
		}
	}
	return statuses, nil
}

// Number represents the information about a phone number
type Number struct {
	Price   float32 `json:"price"`
	Country string  `json:"country"`
}

// CheckNumber is used to get the cost and country for the given phone numbers
func (t TextMagic) CheckNumber(numbers []string) (map[string]Number, error) {
	ns := make(map[string]Number)
	if err := t.sendAPI(cmdCheckNumber, url.Values{"phone": {strings.Join(numbers, joinSep)}}, ns); err != nil {
		return nil, err
	}
	return ns, nil
}

type deleted struct {
	Deleted []string `json:"deleted"`
}

// DeleteReply will simple delete message replies with the given ids
func (t TextMagic) DeleteReply(ids []string) ([]string, error) {
	toRet := make([]string, 0, len(ids))
	for _, tIds := range splitSlice(ids) {
		var d deleted
		if err := t.sendAPI(cmdDeleteReply, url.Values{"deleted": {strings.Join(tIds, joinSep)}}, &d); err != nil {
			return toRet, err
		}
		toRet = append(toRet, d.Deleted...)
	}
	return toRet, nil
}

// Message represents the information about a received message
type Message struct {
	ID        uint64 `json:"message_id"`
	From      string `json:"from"`
	Timestamp int64  `json:"timestamp"`
	Text      string `json:"text"`
}

type received struct {
	Messages []Message `json:"messages"`
	Unread   uint64    `json:"unread"`
}

// Receive will retrieve the number of unread messages and the 100 latest
// replies
func (t TextMagic) Receive(lastRetrieved uint64) (uint64, []Message, error) {
	var r received
	err := t.sendAPI(cmdReceive, url.Values{"last_retrieved_id": {utos(lastRetrieved)}}, &r)
	return r.Unread, r.Messages, err
}

// Option is a type representing a message sending option
type Option func(u url.Values)

// From is an option to modify the sender of a message
func From(from string) Option {
	return func(u url.Values) {
		u.Set("from", from)
	}
}

// MaxLength is an option to limit the length of a message
func MaxLength(length uint64) Option {
	if length > 3 {
		length = 3
	}
	return func(u url.Values) {
		u.Set("max_length", utos(length))
	}
}

// CutExtra sets the option to automatically trim overlong messages
func CutExtra() Option {
	return func(u url.Values) {
		u.Set("cut_extra", "1")
	}
}

// SendTime sets the option to schedule the sending of a message for a specific
// time
func SendTime(t time.Time) Option {
	return func(u url.Values) {
		u.Set("send_time", t.Format(time.RFC3339))
	}
}

type messageResponse struct {
	IDs   map[string]string `json:"message_id"`
	Text  string            `json:"sent_text"`
	Parts uint              `json:"parts_count"`
}

// Send will send a message to the given recipients. It takes options to modify
// the scheduling. sender and length of the message
func (t TextMagic) Send(message string, to []string, options ...Option) (map[string]string, string, uint, error) {
	var (
		params = url.Values{}
		text   string
		parts  uint
		ids    = make(map[string]string)
	)
	// check message for unicode/invalid chars
	params.Set("text", message)
	params.Set("unicode", "0")
	for _, o := range options {
		o(params)
	}
	for _, numbers := range splitSlice(to) {
		params.Set("phone", strings.Join(numbers, joinSep))
		var m messageResponse
		if err := t.sendAPI(cmdSend, params, &m); err != nil {
			return ids, text, parts, err
		}
		if parts == 0 {
			parts = m.Parts
			text = m.Text
		}
		for id, number := range m.IDs {
			ids[id] = number
		}
	}
	return ids, text, parts, nil
}

// Errors

// APIError is an error returned when the incorrect or unexpected data is
// received
type APIError struct {
	Cmd     string
	Code    int    `json:"error_code"`
	Message string `json:"error_message"`
}

func (a APIError) Error() string {
	return "command " + a.Cmd + " returned the following API error: " + a.Message
}

// RequestError is an error which wraps an error that occurs while making an
// API call
type RequestError struct {
	Cmd string
	Err error
}

func (r RequestError) Error() string {
	return "command " + r.Cmd + " returned the following error while makeing the API call: " + r.Err.Error()
}

// StatusError is an error that is returned when a non-200 OK http response is
// received
type StatusError struct {
	Cmd        string
	StatusCode int
}

func (s StatusError) Error() string {
	return "command " + s.Cmd + " returned a non-200 OK response: " + http.StatusText(s.StatusCode)
}

// JSONError is an error that wraps a JSON error
type JSONError struct {
	Cmd string
	Err error
}

func (j JSONError) Error() string {
	return "command " + j.Cmd + " returned malformed JSON: " + j.Err.Error()
}
<|endoftext|>


<commit_before>package check

import (
	"bytes"
	"go/ast"
	"go/parser"
	"go/printer"
	"go/token"
	"os"
)

func indent(s, with string) (r string) {
	eol := true
	for i := 0; i != len(s); i++ {
		c := s[i]
		switch {
		case eol && c == '\n' || c == '\r':
		case c == '\n' || c == '\r':
			eol = true
		case eol:
			eol = false
			s = s[:i] + with + s[i:]
			i += len(with)
		}
	}
	return s
}

func printLine(filename string, line int) (string, error) {
	fset := token.NewFileSet()
	file, err := os.Open(filename)
	if err != nil {
		return "", err
	}
	fnode, err := parser.ParseFile(fset, filename, file, parser.ParseComments)
	if err != nil {
		return "", err
	}
	config := &printer.Config{Mode: printer.UseSpaces, Tabwidth: 4}
	lp := &linePrinter{fset: fset, fnode: fnode, line: line, config: config}
	ast.Walk(lp, fnode)
	result := lp.output.Bytes()
	// Comments leave \n at the end.
	n := len(result)
	for n > 0 && result[n-1] == '\n' {
		n--
	}
	return string(result[:n]), nil
}

type linePrinter struct {
	config *printer.Config
	fset   *token.FileSet
	fnode  *ast.File
	line   int
	output bytes.Buffer
	stmt   ast.Stmt
}

func (lp *linePrinter) emit() bool {
	if lp.stmt != nil {
		lp.trim(lp.stmt)
		lp.printWithComments(lp.stmt)
		lp.stmt = nil
		return true
	}
	return false
}

func (lp *linePrinter) printWithComments(n ast.Node) {
	nfirst := lp.fset.Position(n.Pos()).Line
	nlast := lp.fset.Position(n.End()).Line
	for _, g := range lp.fnode.Comments {
		cfirst := lp.fset.Position(g.Pos()).Line
		clast := lp.fset.Position(g.End()).Line
		if clast == nfirst-1 && lp.fset.Position(n.Pos()).Column == lp.fset.Position(g.Pos()).Column {
			for _, c := range g.List {
				lp.output.WriteString(c.Text)
				lp.output.WriteByte('\n')
			}
		}
		if cfirst >= nfirst && cfirst <= nlast && n.End() <= g.List[0].Slash {
			// The printer will not include the comment if it starts past
			// the node itself. Trick it into printing by overlapping the
			// slash with the end of the statement.
			g.List[0].Slash = n.End() - 1
		}
	}
	node := &printer.CommentedNode{n, lp.fnode.Comments}
	lp.config.Fprint(&lp.output, lp.fset, node)
}

func (lp *linePrinter) Visit(n ast.Node) (w ast.Visitor) {
	if n == nil {
		if lp.output.Len() == 0 {
			lp.emit()
		}
		return nil
	}
	first := lp.fset.Position(n.Pos()).Line
	last := lp.fset.Position(n.End()).Line
	if first <= lp.line && last >= lp.line {
		// Print the innermost statement containing the line.
		if stmt, ok := n.(ast.Stmt); ok {
			if _, ok := n.(*ast.BlockStmt); !ok {
				lp.stmt = stmt
			}
		}
		if first == lp.line && lp.emit() {
			return nil
		}
		return lp
	}
	return nil
}

func (lp *linePrinter) trim(n ast.Node) bool {
	stmt, ok := n.(ast.Stmt)
	if !ok {
		return true
	}
	line := lp.fset.Position(n.Pos()).Line
	if line != lp.line {
		return false
	}
	switch stmt := stmt.(type) {
	case *ast.IfStmt:
		stmt.Body = lp.trimBlock(stmt.Body)
	case *ast.SwitchStmt:
		stmt.Body = lp.trimBlock(stmt.Body)
	case *ast.TypeSwitchStmt:
		stmt.Body = lp.trimBlock(stmt.Body)
	case *ast.CaseClause:
		stmt.Body = lp.trimList(stmt.Body)
	case *ast.CommClause:
		stmt.Body = lp.trimList(stmt.Body)
	case *ast.BlockStmt:
		stmt.List = lp.trimList(stmt.List)
	}
	return true
}

func (lp *linePrinter) trimBlock(stmt *ast.BlockStmt) *ast.BlockStmt {
	if !lp.trim(stmt) {
		return lp.emptyBlock(stmt)
	}
	stmt.Rbrace = stmt.Lbrace
	return stmt
}

func (lp *linePrinter) trimList(stmts []ast.Stmt) []ast.Stmt {
	for i := 0; i != len(stmts); i++ {
		if !lp.trim(stmts[i]) {
			stmts[i] = lp.emptyStmt(stmts[i])
			break
		}
	}
	return stmts
}

func (lp *linePrinter) emptyStmt(n ast.Node) *ast.ExprStmt {
	return &ast.ExprStmt{&ast.Ellipsis{n.Pos(), nil}}
}

func (lp *linePrinter) emptyBlock(n ast.Node) *ast.BlockStmt {
	p := n.Pos()
	return &ast.BlockStmt{p, []ast.Stmt{lp.emptyStmt(n)}, p}
}
<commit_msg>Fixed IDE warnings<commit_after>package check

import (
	"bytes"
	"go/ast"
	"go/parser"
	"go/printer"
	"go/token"
	"os"
)

func indent(s, with string) (r string) {
	eol := true
	for i := 0; i != len(s); i++ {
		c := s[i]
		switch {
		case eol && c == '\n' || c == '\r':
		case c == '\n' || c == '\r':
			eol = true
		case eol:
			eol = false
			s = s[:i] + with + s[i:]
			i += len(with)
		}
	}
	return s
}

func printLine(filename string, line int) (string, error) {
	fset := token.NewFileSet()
	file, err := os.Open(filename)
	if err != nil {
		return "", err
	}
	fnode, err := parser.ParseFile(fset, filename, file, parser.ParseComments)
	if err != nil {
		return "", err
	}
	config := &printer.Config{Mode: printer.UseSpaces, Tabwidth: 4}
	lp := &linePrinter{fset: fset, fnode: fnode, line: line, config: config}
	ast.Walk(lp, fnode)
	result := lp.output.Bytes()
	// Comments leave \n at the end.
	n := len(result)
	for n > 0 && result[n-1] == '\n' {
		n--
	}
	return string(result[:n]), nil
}

type linePrinter struct {
	config *printer.Config
	fset   *token.FileSet
	fnode  *ast.File
	line   int
	output bytes.Buffer
	stmt   ast.Stmt
}

func (lp *linePrinter) emit() bool {
	if lp.stmt != nil {
		lp.trim(lp.stmt)
		lp.printWithComments(lp.stmt)
		lp.stmt = nil
		return true
	}
	return false
}

func (lp *linePrinter) printWithComments(n ast.Node) {
	nfirst := lp.fset.Position(n.Pos()).Line
	nlast := lp.fset.Position(n.End()).Line
	for _, g := range lp.fnode.Comments {
		cfirst := lp.fset.Position(g.Pos()).Line
		clast := lp.fset.Position(g.End()).Line
		if clast == nfirst-1 && lp.fset.Position(n.Pos()).Column == lp.fset.Position(g.Pos()).Column {
			for _, c := range g.List {
				lp.output.WriteString(c.Text)
				lp.output.WriteByte('\n')
			}
		}
		if cfirst >= nfirst && cfirst <= nlast && n.End() <= g.List[0].Slash {
			// The printer will not include the comment if it starts past
			// the node itself. Trick it into printing by overlapping the
			// slash with the end of the statement.
			g.List[0].Slash = n.End() - 1
		}
	}
	node := &printer.CommentedNode{n, lp.fnode.Comments}
	lp.config.Fprint(&lp.output, lp.fset, node)
}

func (lp *linePrinter) Visit(n ast.Node) (w ast.Visitor) {
	if n == nil {
		if lp.output.Len() == 0 {
			lp.emit()
		}
		return nil
	}
	first := lp.fset.Position(n.Pos()).Line
	last := lp.fset.Position(n.End()).Line
	if first <= lp.line && last >= lp.line {
		// Print the innermost statement containing the line.
		if stmt, ok := n.(ast.Stmt); ok {
			if _, ok := n.(*ast.BlockStmt); !ok {
				lp.stmt = stmt
			}
		}
		if first == lp.line && lp.emit() {
			return nil
		}
		return lp
	}
	return nil
}

func (lp *linePrinter) trim(n ast.Node) bool {
	s, ok := n.(ast.Stmt)
	if !ok {
		return true
	}
	line := lp.fset.Position(n.Pos()).Line
	if line != lp.line {
		return false
	}
	switch stmt := s.(type) {
	case *ast.IfStmt:
		stmt.Body = lp.trimBlock(stmt.Body)
	case *ast.SwitchStmt:
		stmt.Body = lp.trimBlock(stmt.Body)
	case *ast.TypeSwitchStmt:
		stmt.Body = lp.trimBlock(stmt.Body)
	case *ast.CaseClause:
		stmt.Body = lp.trimList(stmt.Body)
	case *ast.CommClause:
		stmt.Body = lp.trimList(stmt.Body)
	case *ast.BlockStmt:
		stmt.List = lp.trimList(stmt.List)
	}
	return true
}

func (lp *linePrinter) trimBlock(stmt *ast.BlockStmt) *ast.BlockStmt {
	if !lp.trim(stmt) {
		return lp.emptyBlock(stmt)
	}
	stmt.Rbrace = stmt.Lbrace
	return stmt
}

func (lp *linePrinter) trimList(stmts []ast.Stmt) []ast.Stmt {
	for i := 0; i != len(stmts); i++ {
		if !lp.trim(stmts[i]) {
			stmts[i] = lp.emptyStmt(stmts[i])
			break
		}
	}
	return stmts
}

func (lp *linePrinter) emptyStmt(n ast.Node) *ast.ExprStmt {
	return &ast.ExprStmt{&ast.Ellipsis{n.Pos(), nil}}
}

func (lp *linePrinter) emptyBlock(n ast.Node) *ast.BlockStmt {
	p := n.Pos()
	return &ast.BlockStmt{p, []ast.Stmt{lp.emptyStmt(n)}, p}
}
<|endoftext|>


<commit_before>package ifrit

import (
	"fmt"
	"os"
	"sync"
)

type Process interface {
	Wait() <-chan error
	Signal(os.Signal)
}

func Envoke(r Runner) Process {
	return envokeProcess(r)
}

func envokeProcess(r Runner) Process {
	p := &process{
		runner:         r,
		sig:            make(chan os.Signal),
		exitStatusChan: make(chan error, 1),
		ready:          make(chan struct{}),
	}
	go p.run()

	select {
	case <-p.ready:
	case <-p.Wait():
	}

	return p
}

type process struct {
	runner         Runner
	sig            chan os.Signal
	exitStatus     error
	exitStatusChan chan error
	ready          chan struct{}
	exitOnce       sync.Once
}

func (p *process) run() {
	defer func() {
		msg := recover()
		if msg != nil {
			p.exitStatusChan <- fmt.Errorf("%s", msg)
		}
	}()

	p.exitStatusChan <- p.runner.Run(p.sig, p.ready)
}

func (p *process) getExitStatus() error {
	p.exitOnce.Do(func() {
		p.exitStatus = <-p.exitStatusChan
	})
	return p.exitStatus
}

func (p *process) Wait() <-chan error {
	exitChan := make(chan error, 1)

	go func() {
		exitChan <- p.getExitStatus()
	}()

	return exitChan
}

func (p *process) Signal(signal os.Signal) {
	go func() {
		p.sig <- signal
	}()
}
<commit_msg>do not recover panics<commit_after>package ifrit

import (
	"os"
	"sync"
)

type Process interface {
	Wait() <-chan error
	Signal(os.Signal)
}

func Envoke(r Runner) Process {
	return envokeProcess(r)
}

func envokeProcess(r Runner) Process {
	p := &process{
		runner:         r,
		sig:            make(chan os.Signal),
		exitStatusChan: make(chan error, 1),
		ready:          make(chan struct{}),
	}
	go p.run()

	select {
	case <-p.ready:
	case <-p.Wait():
	}

	return p
}

type process struct {
	runner         Runner
	sig            chan os.Signal
	exitStatus     error
	exitStatusChan chan error
	ready          chan struct{}
	exitOnce       sync.Once
}

func (p *process) run() {
	p.exitStatusChan <- p.runner.Run(p.sig, p.ready)
}

func (p *process) getExitStatus() error {
	p.exitOnce.Do(func() {
		p.exitStatus = <-p.exitStatusChan
	})
	return p.exitStatus
}

func (p *process) Wait() <-chan error {
	exitChan := make(chan error, 1)

	go func() {
		exitChan <- p.getExitStatus()
	}()

	return exitChan
}

func (p *process) Signal(signal os.Signal) {
	go func() {
		p.sig <- signal
	}()
}
<|endoftext|>


<commit_before>package upgrade

import (
	"os"

	log "github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
	"github.com/flexiant/kdeploy/template"
	"github.com/flexiant/kdeploy/upgrade/strategies"
	"github.com/flexiant/kdeploy/utils"
	"github.com/flexiant/kdeploy/webservice"
	"github.com/hashicorp/go-version"
)

// CmdUpgrade implements 'upgrade' command
func CmdUpgrade(c *cli.Context) {
	localKubePath, err := webservice.FetchKubeFromURL(os.Getenv("KDEPLOY_KUBEWARE"))
	utils.CheckError(err)

	log.Debugf("Going to parse kubeware in %s", localKubePath)

	md := template.ParseMetadata(localKubePath)
	utils.CheckError(err)

	kubernetes, err := webservice.NewKubeClient()
	utils.CheckError(err)

	namespace := os.Getenv("KDEPLOY_NAMESPACE")
	// labelSelector := fmt.Sprintf("kubeware=%s,kubeware-version=%s", md.Name, md.Version)

	// Check if kubeware already installed, error if it's not
	v, err := kubernetes.FindDeployedKubewareVersion(namespace, md.Name)
	utils.CheckError(err)
	log.Infof("Found version %s of %s.%s", v, namespace, md.Name)

	// Check if equal or newer version already exists, error if so
	deployedVersion, err := version.NewVersion(v)
	utils.CheckError(err)
	upgradeVersion, err := version.NewVersion(md.Version)
	utils.CheckError(err)
	if upgradeVersion.LessThan(deployedVersion) {
		log.Fatalf("Can not upgrade to version '%s' since version '%s' is already deployed", md.Version, v)
	}

	// build attributes merging "role list" to defaults
	log.Debugf("Building attributes")
	defaults, err := md.AttributeDefaults()
	utils.CheckError(err)
	attributes := template.BuildAttributes(c.String("attribute"), defaults)

	// get services and parse each one
	log.Debugf("Parsing services")
	servicesSpecs, err := md.ParseServices(attributes)
	utils.CheckError(err)

	// get replica controllers and parse each one
	log.Debugf("Parsing controllers")
	controllersSpecs, err := md.ParseControllers(attributes)
	utils.CheckError(err)

	// upgStrategy := upgradeStrategies.RecreateAllStrategy(kubernetes)
	upgStrategy := upgradeStrategies.RollRcPatchSvcStrategy(kubernetes, 1)
	upgStrategy.Upgrade(namespace, servicesSpecs, controllersSpecs)
}
<commit_msg>recreateAll upgrade strategy by default<commit_after>package upgrade

import (
	"os"

	log "github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
	"github.com/flexiant/kdeploy/template"
	"github.com/flexiant/kdeploy/upgrade/strategies"
	"github.com/flexiant/kdeploy/utils"
	"github.com/flexiant/kdeploy/webservice"
	"github.com/hashicorp/go-version"
)

// CmdUpgrade implements 'upgrade' command
func CmdUpgrade(c *cli.Context) {
	localKubePath, err := webservice.FetchKubeFromURL(os.Getenv("KDEPLOY_KUBEWARE"))
	utils.CheckError(err)

	log.Debugf("Going to parse kubeware in %s", localKubePath)

	md := template.ParseMetadata(localKubePath)
	utils.CheckError(err)

	kubernetes, err := webservice.NewKubeClient()
	utils.CheckError(err)

	namespace := os.Getenv("KDEPLOY_NAMESPACE")
	// labelSelector := fmt.Sprintf("kubeware=%s,kubeware-version=%s", md.Name, md.Version)

	// Check if kubeware already installed, error if it's not
	v, err := kubernetes.FindDeployedKubewareVersion(namespace, md.Name)
	utils.CheckError(err)
	log.Infof("Found version %s of %s.%s", v, namespace, md.Name)

	// Check if equal or newer version already exists, error if so
	deployedVersion, err := version.NewVersion(v)
	utils.CheckError(err)
	upgradeVersion, err := version.NewVersion(md.Version)
	utils.CheckError(err)
	if upgradeVersion.LessThan(deployedVersion) {
		log.Fatalf("Can not upgrade to version '%s' since version '%s' is already deployed", md.Version, v)
	}

	// build attributes merging "role list" to defaults
	log.Debugf("Building attributes")
	defaults, err := md.AttributeDefaults()
	utils.CheckError(err)
	attributes := template.BuildAttributes(c.String("attribute"), defaults)

	// get services and parse each one
	log.Debugf("Parsing services")
	servicesSpecs, err := md.ParseServices(attributes)
	utils.CheckError(err)

	// get replica controllers and parse each one
	log.Debugf("Parsing controllers")
	controllersSpecs, err := md.ParseControllers(attributes)
	utils.CheckError(err)

	upgStrategy := upgradeStrategies.RecreateAllStrategy(kubernetes)
	// upgStrategy := upgradeStrategies.RollRcPatchSvcStrategy(kubernetes, 1)
	upgStrategy.Upgrade(namespace, servicesSpecs, controllersSpecs)
}
<|endoftext|>


<commit_before>// Copyright (c) 2013-2019 by Michael Dvorkin and contributors. All Rights Reserved.
// Use of this source code is governed by a MIT-style license that can
// be found in the LICENSE file.

package mop

import (
	"encoding/json"
	"io/ioutil"
	"sort"
	"strings"

	"github.com/Knetic/govaluate"
)

const defaultGainColor = "green"
const defaultLossColor = "red"
const defaultTagColor = "yellow"

// Profile manages Mop program settings as defined by user (ex. list of
// stock tickers). The settings are serialized using JSON and saved in
// the ~/.moprc file.
type Profile struct {
	Tickers          []string                       // List of stock tickers to display.
	MarketRefresh    int                            // Time interval to refresh market data.
	QuotesRefresh    int                            // Time interval to refresh stock quotes.
	SortColumn       int                            // Column number by which we sort stock quotes.
	Ascending        bool                           // True when sort order is ascending.
	Grouped          bool                           // True when stocks are grouped by advancing/declining.
	Filter           string                         // Filter in human form
	Colors           struct {                       // User defined colors
		Gain             string
		Loss             string
		Tag              string
	}
	filterExpression *govaluate.EvaluableExpression // The filter as a govaluate expression
	selectedColumn   int                            // Stores selected column number when the column editor is active.
	filename         string                         // Path to the file in which the configuration is stored
}

func IsSupportedColor(colorName string) bool {
	switch colorName {
		case
			"black",
			"red",
			"green",
			"yellow",
			"blue",
			"magenta",
			"cyan",
			"white",
			"darkgray",
			"lightred",
			"lightgreen",
			"lightyellow",
			"lightblue",
			"lightmagenta",
			"lightcyan",
			"lightgray":
			return true
	}
	return false
}

// Creates the profile and attempts to load the settings from ~/.moprc file.
// If the file is not there it gets created with default values.
func NewProfile(filename string) *Profile {
	profile := &Profile{filename: filename}
	data, err := ioutil.ReadFile(filename)
	if err != nil { // Set default values:
		profile.MarketRefresh = 12 // Market data gets fetched every 12s (5 times per minute).
		profile.QuotesRefresh = 5  // Stock quotes get updated every 5s (12 times per minute).
		profile.Grouped = false    // Stock quotes are *not* grouped by advancing/declining.
		profile.Tickers = []string{`AAPL`, `C`, `GOOG`, `IBM`, `KO`, `ORCL`, `V`}
		profile.SortColumn = 0   // Stock quotes are sorted by ticker name.
		profile.Ascending = true // A to Z.
		profile.Filter = ""
		profile.Colors.Gain = defaultGainColor
		profile.Colors.Loss = defaultLossColor
		profile.Colors.Tag = defaultTagColor
		profile.Save()
	} else {
		json.Unmarshal(data, profile)

		profile.Colors.Gain = strings.ToLower(profile.Colors.Gain)
		if !IsSupportedColor(profile.Colors.Gain) {
			profile.Colors.Gain = defaultGainColor
		}

		profile.Colors.Loss = strings.ToLower(profile.Colors.Loss)
		if !IsSupportedColor(profile.Colors.Loss) {
			profile.Colors.Loss = defaultLossColor
		}

		profile.Colors.Tag = strings.ToLower(profile.Colors.Tag)
		if !IsSupportedColor(profile.Colors.Tag) {
			profile.Colors.Tag = defaultTagColor
		}

		profile.SetFilter(profile.Filter)
	}
	profile.selectedColumn = -1

	return profile
}

// Save serializes settings using JSON and saves them in ~/.moprc file.
func (profile *Profile) Save() error {
	data, err := json.MarshalIndent(profile, "", "    ")
	if err != nil {
		return err
	}

	return ioutil.WriteFile(profile.filename, data, 0644)
}

// AddTickers updates the list of existing tickers to add the new ones making
// sure there are no duplicates.
func (profile *Profile) AddTickers(tickers []string) (added int, err error) {
	added, err = 0, nil
	existing := make(map[string]bool)

	// Build a hash of existing tickers so we could look it up quickly.
	for _, ticker := range profile.Tickers {
		existing[ticker] = true
	}

	// Iterate over the list of new tickers excluding the ones that
	// already exist.
	for _, ticker := range tickers {
		if _, found := existing[ticker]; !found {
			profile.Tickers = append(profile.Tickers, ticker)
			added++
		}
	}

	if added > 0 {
		sort.Strings(profile.Tickers)
		err = profile.Save()
	}

	return
}

// RemoveTickers removes requested stock tickers from the list we track.
func (profile *Profile) RemoveTickers(tickers []string) (removed int, err error) {
	removed, err = 0, nil
	for _, ticker := range tickers {
		for i, existing := range profile.Tickers {
			if ticker == existing {
				// Requested ticker is there: remove i-th slice item.
				profile.Tickers = append(profile.Tickers[:i], profile.Tickers[i+1:]...)
				removed++
			}
		}
	}

	if removed > 0 {
		err = profile.Save()
	}

	return
}

// Reorder gets called by the column editor to either reverse sorting order
// for the current column, or to pick another sort column.
func (profile *Profile) Reorder() error {
	if profile.selectedColumn == profile.SortColumn {
		profile.Ascending = !profile.Ascending // Reverse sort order.
	} else {
		profile.SortColumn = profile.selectedColumn // Pick new sort column.
	}
	return profile.Save()
}

// Regroup flips the flag that controls whether the stock quotes are grouped
// by advancing/declining issues.
func (profile *Profile) Regroup() error {
	profile.Grouped = !profile.Grouped
	return profile.Save()
}

// SetFilter creates a govaluate.EvaluableExpression.
func (profile *Profile) SetFilter(filter string) {
	if len(filter) > 0 {
		var err error
		profile.filterExpression, err = govaluate.NewEvaluableExpression(filter)

		if err != nil {
			panic(err)
		}

	} else if len(filter) == 0 && profile.filterExpression != nil {
		profile.filterExpression = nil
	}

	profile.Filter = filter
}
<commit_msg>Factorize code to init sementic colors<commit_after>// Copyright (c) 2013-2019 by Michael Dvorkin and contributors. All Rights Reserved.
// Use of this source code is governed by a MIT-style license that can
// be found in the LICENSE file.

package mop

import (
	"encoding/json"
	"io/ioutil"
	"sort"
	"strings"

	"github.com/Knetic/govaluate"
)

const defaultGainColor = "green"
const defaultLossColor = "red"
const defaultTagColor = "yellow"

// Profile manages Mop program settings as defined by user (ex. list of
// stock tickers). The settings are serialized using JSON and saved in
// the ~/.moprc file.
type Profile struct {
	Tickers          []string                       // List of stock tickers to display.
	MarketRefresh    int                            // Time interval to refresh market data.
	QuotesRefresh    int                            // Time interval to refresh stock quotes.
	SortColumn       int                            // Column number by which we sort stock quotes.
	Ascending        bool                           // True when sort order is ascending.
	Grouped          bool                           // True when stocks are grouped by advancing/declining.
	Filter           string                         // Filter in human form
	Colors           struct {                       // User defined colors
		Gain             string
		Loss             string
		Tag              string
	}
	filterExpression *govaluate.EvaluableExpression // The filter as a govaluate expression
	selectedColumn   int                            // Stores selected column number when the column editor is active.
	filename         string                         // Path to the file in which the configuration is stored
}

func IsSupportedColor(colorName string) bool {
	switch colorName {
		case
			"black",
			"red",
			"green",
			"yellow",
			"blue",
			"magenta",
			"cyan",
			"white",
			"darkgray",
			"lightred",
			"lightgreen",
			"lightyellow",
			"lightblue",
			"lightmagenta",
			"lightcyan",
			"lightgray":
			return true
	}
	return false
}

// Creates the profile and attempts to load the settings from ~/.moprc file.
// If the file is not there it gets created with default values.
func NewProfile(filename string) *Profile {
	profile := &Profile{filename: filename}
	data, err := ioutil.ReadFile(filename)
	if err != nil { // Set default values:
		profile.MarketRefresh = 12 // Market data gets fetched every 12s (5 times per minute).
		profile.QuotesRefresh = 5  // Stock quotes get updated every 5s (12 times per minute).
		profile.Grouped = false    // Stock quotes are *not* grouped by advancing/declining.
		profile.Tickers = []string{`AAPL`, `C`, `GOOG`, `IBM`, `KO`, `ORCL`, `V`}
		profile.SortColumn = 0   // Stock quotes are sorted by ticker name.
		profile.Ascending = true // A to Z.
		profile.Filter = ""
		profile.Colors.Gain = defaultGainColor
		profile.Colors.Loss = defaultLossColor
		profile.Colors.Tag = defaultTagColor
		profile.Save()
	} else {
		json.Unmarshal(data, profile)

		InitColor(profile.Colors.Gain, defaultGainColor)
		InitColor(profile.Colors.Loss, defaultLossColor)
		InitColor(profile.Colors.Tag, defaultTagColor)

		profile.SetFilter(profile.Filter)
	}
	profile.selectedColumn = -1

	return profile
}

func InitColor(color string, defaultValue string) {
	color = strings.ToLower(color)
	if !IsSupportedColor(color) {
		color = defaultValue;
	}
}

// Save serializes settings using JSON and saves them in ~/.moprc file.
func (profile *Profile) Save() error {
	data, err := json.MarshalIndent(profile, "", "    ")
	if err != nil {
		return err
	}

	return ioutil.WriteFile(profile.filename, data, 0644)
}

// AddTickers updates the list of existing tickers to add the new ones making
// sure there are no duplicates.
func (profile *Profile) AddTickers(tickers []string) (added int, err error) {
	added, err = 0, nil
	existing := make(map[string]bool)

	// Build a hash of existing tickers so we could look it up quickly.
	for _, ticker := range profile.Tickers {
		existing[ticker] = true
	}

	// Iterate over the list of new tickers excluding the ones that
	// already exist.
	for _, ticker := range tickers {
		if _, found := existing[ticker]; !found {
			profile.Tickers = append(profile.Tickers, ticker)
			added++
		}
	}

	if added > 0 {
		sort.Strings(profile.Tickers)
		err = profile.Save()
	}

	return
}

// RemoveTickers removes requested stock tickers from the list we track.
func (profile *Profile) RemoveTickers(tickers []string) (removed int, err error) {
	removed, err = 0, nil
	for _, ticker := range tickers {
		for i, existing := range profile.Tickers {
			if ticker == existing {
				// Requested ticker is there: remove i-th slice item.
				profile.Tickers = append(profile.Tickers[:i], profile.Tickers[i+1:]...)
				removed++
			}
		}
	}

	if removed > 0 {
		err = profile.Save()
	}

	return
}

// Reorder gets called by the column editor to either reverse sorting order
// for the current column, or to pick another sort column.
func (profile *Profile) Reorder() error {
	if profile.selectedColumn == profile.SortColumn {
		profile.Ascending = !profile.Ascending // Reverse sort order.
	} else {
		profile.SortColumn = profile.selectedColumn // Pick new sort column.
	}
	return profile.Save()
}

// Regroup flips the flag that controls whether the stock quotes are grouped
// by advancing/declining issues.
func (profile *Profile) Regroup() error {
	profile.Grouped = !profile.Grouped
	return profile.Save()
}

// SetFilter creates a govaluate.EvaluableExpression.
func (profile *Profile) SetFilter(filter string) {
	if len(filter) > 0 {
		var err error
		profile.filterExpression, err = govaluate.NewEvaluableExpression(filter)

		if err != nil {
			panic(err)
		}

	} else if len(filter) == 0 && profile.filterExpression != nil {
		profile.filterExpression = nil
	}

	profile.Filter = filter
}
<|endoftext|>


<commit_before>package core

import (
	"bytes"
	"fmt"
	"io"
	"strings"
	"time"
)

// Note represents a musical note.
// Notations:
// 		2.C#3 = half duration, pitch C, sharp, octave 3, velocity default
//		D     = quarter duration, pitch D, octave 4, no accidental
//      8B_   = eighth duration, pitch B, octave 4, flat
//		=     = quarter rest
//      -/+   = velocity number
// http://en.wikipedia.org/wiki/Musical_Note
type Note struct {
	Name       string // {C D E F G A B = ^ < >}
	Octave     int
	Accidental int  // -1 Flat, +1 Sharp, 0 Normal
	Dotted     bool // if true then fraction is increased by half
	Velocity   int  // 1..127

	fraction float32       // {0.0625,0.125,0.25,0.5,1}
	duration time.Duration // if set then this overrides Dotted and fraction
}

func (n Note) Storex() string {
	return fmt.Sprintf("note('%s')", n.String())
}

// ToNote() is part of NoteConvertable
func (n Note) ToNote() (Note, error) {
	return n, nil
}

func (n Note) ToRest() Note {
	return Note{
		Name:       "=",
		Octave:     n.Octave,
		Accidental: n.Accidental,
		Dotted:     n.Dotted,
		Velocity:   n.Velocity,
		fraction:   n.fraction,
		duration:   n.duration,
	}
}

// Replaced is part of Replaceable
func (n Note) Replaced(from, to Sequenceable) Sequenceable {
	if IsIdenticalTo(from, n) {
		return to
	}
	return n
}

var (
	Rest4        = Note{Name: "=", fraction: 0.25}
	PedalUpDown  = Note{Name: "^", fraction: 0}
	PedalDown    = Note{Name: ">", fraction: 0}
	PedalUp      = Note{Name: "<", fraction: 0}
	ZeroDuration = time.Duration(0)
)

func NewNote(name string, octave int, frac float32, accidental int, dot bool, velocity int) (Note, error) {
	if len(name) != 1 {
		return Rest4, fmt.Errorf("note must be one character, got [%s]", name)
	}
	// pedal check
	switch name {
	case "^":
		return PedalUpDown, nil
	case ">":
		return PedalDown, nil
	case "<":
		return PedalUp, nil
	}

	if !strings.Contains(allowedNoteNames, name) {
		return Rest4, fmt.Errorf("invalid note name [%s]:%s", allowedNoteNames, name)
	}
	switch frac {
	case 0.0625:
	case 0.125:
	case 0.25:
	case 0.5:
	case 1:
	default:
		return Rest4, fmt.Errorf("invalid fraction [1,0.5,0.25,0.125,0.0625]:%v", frac)
	}

	if accidental != 0 && accidental != -1 && accidental != 1 {
		return Rest4, fmt.Errorf("invalid accidental: %d", accidental)
	}
	return Note{Name: name, Octave: octave, fraction: frac, Accidental: accidental, Dotted: dot, Velocity: velocity}, nil
}

func MakeNote(name string, octave int, frac float32, accidental int, dot bool, velocity int) Note {
	return Note{Name: name, Octave: octave, fraction: frac, Accidental: accidental, Dotted: dot, Velocity: velocity}
}

func (n Note) IsRest() bool        { return Rest4.Name == n.Name }
func (n Note) IsPedalUp() bool     { return PedalUp.Name == n.Name }
func (n Note) IsPedalDown() bool   { return PedalDown.Name == n.Name }
func (n Note) IsPedalUpDown() bool { return PedalUpDown.Name == n.Name }
func (n Note) IsPedal() bool {
	return PedalUpDown.Name == n.Name || PedalDown.Name == n.Name || PedalUp.Name == n.Name
}

// DurationFactor is the actual duration time factor
func (n Note) DurationFactor() float32 {
	if n.Dotted {
		return n.fraction * 1.5
	}
	return n.fraction
}

func (n Note) S() Sequence {
	return BuildSequence([]Note{n})
}

func (n Note) WithDynamic(emphasis string) Note {
	n.Velocity = ParseVelocity(emphasis)
	return n
}

func (n Note) WithVelocity(v int) Note {
	n.Velocity = v
	return n
}

func (n Note) WithFraction(f float32, dotted bool) Note {
	// TODO
	if f == 0.5*1.5 {
		n.fraction = 0.5
		n.Dotted = true
		return n
	}
	if f == 0.25*1.5 {
		n.fraction = 0.25
		n.Dotted = true
		return n
	}
	if f == 0.125*1.5 {
		n.fraction = 0.125
		n.Dotted = true
		return n
	}
	if f == 0.0625*1.5 {
		n.fraction = 0.0625
		n.Dotted = true
		return n
	}
	n.fraction = f
	n.Dotted = dotted
	return n
}

func (n Note) IsHearable() bool {
	return strings.ContainsAny(n.Name, "ABCDEFG")
}

// MustParseNote returns a Note by parsing the input. Panic if it fails.
func MustParseNote(input string) Note {
	n, err := ParseNote(input)
	if err != nil {
		panic("MustParseNote failed:" + err.Error())
	}
	return n
}

var N = MustParseNote

// ParseNote reads the format  <(inverse-)duration?>[CDEFGA=<^>]<accidental?><dot?><octave?>
func ParseNote(input string) (Note, error) {
	return newFormatParser(input).parseNote()
}

func ParseVelocity(plusmin string) (velocity int) {
	switch plusmin {
	case "--":
		velocity = VelocityP
	case "---":
		velocity = VelocityPP
	case "----":
		velocity = VelocityPPP
	case "-----":
		velocity = VelocityPPPP
	case "++":
		velocity = VelocityF
	case "+++":
		velocity = VelocityFF
	case "++++":
		velocity = VelocityFFF
	case "+++++":
		velocity = VelocityFFFF
	case "o":
		velocity = Normal
	case "-":
		velocity = VelocityMP
	case "+":
		velocity = VelocityMF
	default:
		// invalid
		velocity = -1
	}
	return
}

// Formatting

func (n Note) accidentalf(encoded bool) string {
	if n.Accidental == -1 {
		if encoded {
			return "b"
		} else {
			return "_"
		}
	}
	if n.Accidental == 1 {
		if encoded {
			return "#"
		} else {
			return "#"
		}
	}
	return ""
}

func (n Note) NonFractionBasedDuration() (time.Duration, bool) {
	if n.duration > 0 {
		return n.duration, true
	}
	return ZeroDuration, false
}

func (n Note) durationf() string {
	switch n.fraction {
	case 0.0625:
		return "16"
	case 0.125:
		return "8"
	case 0.25:
		return "4"
	case 0.5:
		return "2"
	case 1.0:
		return "1"
	}
	return ""
}

func (n Note) Inspect(i Inspection) {
	i.Properties["length"] = n.DurationFactor()
	i.Properties["midi"] = n.MIDI()
	i.Properties["velocity"] = n.Velocity
	wholeNoteDuration := WholeNoteDuration(i.Context.Control().BPM())
	i.Properties["duration"] = time.Duration(float32(wholeNoteDuration) * n.DurationFactor())
}

func (n Note) String() string {
	var buf bytes.Buffer
	n.printOn(&buf, PrintAsSpecified)
	return buf.String()
}

func (n Note) printOn(buf *bytes.Buffer, sharpOrFlatKey int) {
	if n.IsPedalUp() {
		buf.WriteString(PedalUp.Name)
		return
	}
	if n.IsPedalDown() {
		buf.WriteString(PedalDown.Name)
		return
	}
	if n.IsPedalUpDown() {
		buf.WriteString(PedalUpDown.Name)
		return
	}

	if n.fraction != 0.25 {
		buf.WriteString(n.durationf())
	}

	if n.Dotted {
		buf.WriteString(".")
	}

	if n.IsRest() {
		buf.WriteString(n.Name)
		return
	}
	if Sharp == sharpOrFlatKey && n.Accidental == -1 { // want Sharp, specified in Flat
		buf.WriteString(n.Pitched(-1).Name)
		buf.WriteString("#")
	} else if Flat == sharpOrFlatKey && n.Accidental == 1 { // want Flat, specified in Sharp
		buf.WriteString(n.Pitched(1).Name)
		buf.WriteString("_")
	} else { // PrintAsSpecified
		buf.WriteString(n.Name)
		if n.Accidental != 0 {
			buf.WriteString(n.accidentalf(false))
		}
	}
	if n.Octave != 4 {
		fmt.Fprintf(buf, "%d", n.Octave)
	}
	if n.Velocity != Normal {
		switch {
		case n.Velocity <= VelocityPPPP:
			io.WriteString(buf, "-----")
		case n.Velocity <= VelocityPPP:
			io.WriteString(buf, "----")
		case n.Velocity <= VelocityPP:
			io.WriteString(buf, "---")
		case n.Velocity <= VelocityP:
			io.WriteString(buf, "--")
		case n.Velocity <= VelocityMP:
			io.WriteString(buf, "-")
		case n.Velocity <= Normal:
		case n.Velocity <= VelocityMF:
			io.WriteString(buf, "+")
		case n.Velocity <= VelocityF:
			io.WriteString(buf, "++")
		case n.Velocity <= VelocityFF:
			io.WriteString(buf, "+++")
		case n.Velocity <= VelocityFFF:
			io.WriteString(buf, "++++")
		case n.Velocity > VelocityFFF:
			io.WriteString(buf, "+++++")
		}
	}
}

var fractionRanges = []struct {
	fraction float32
	dotted   bool
}{
	{0.0625, false}, // 1/16
	{0.09375, true},
	{0.125, false},
	{0.1875, true},
	{0.25, false},
	{0.375, true},
	{0.5, false},
	{0.75, true},
	{1.0, false},
	{1.5, true},
	{2.0, false}, // non-exist
}

func QuantizeFraction(durationFactor float32) (fraction float32, dotted bool, ok bool) {
	last := float32(0.0)
	for i := 0; i < len(fractionRanges); i++ {
		next := fractionRanges[i]
		halfway := (last + next.fraction) / 2.0
		if durationFactor <= halfway {
			if i == 0 {
				return 0.0, false, false
			}
			prev := fractionRanges[i-1]
			return prev.fraction, prev.dotted, true
		}
		last = next.fraction
	}
	return 0.0, false, false
}
<commit_msg>work on decompose<commit_after>package core

import (
	"bytes"
	"fmt"
	"io"
	"strings"
	"time"
)

// Note represents a musical note.
// Notations:
// 		2.C#3 = half duration, pitch C, sharp, octave 3, velocity default
//		D     = quarter duration, pitch D, octave 4, no accidental
//      8B_   = eighth duration, pitch B, octave 4, flat
//		=     = quarter rest
//      -/+   = velocity number
// http://en.wikipedia.org/wiki/Musical_Note
type Note struct {
	Name       string // {C D E F G A B = ^ < >}
	Octave     int
	Accidental int  // -1 Flat, +1 Sharp, 0 Normal
	Dotted     bool // if true then fraction is increased by half
	Velocity   int  // 1..127

	fraction float32       // {0.0625,0.125,0.25,0.5,1}
	duration time.Duration // if set then this overrides Dotted and fraction
}

func (n Note) Storex() string {
	return fmt.Sprintf("note('%s')", n.String())
}

// ToNote() is part of NoteConvertable
func (n Note) ToNote() (Note, error) {
	return n, nil
}

func (n Note) Fraction() float32 { return n.fraction }

func (n Note) ToRest() Note {
	return Note{
		Name:       "=",
		Octave:     n.Octave,
		Accidental: n.Accidental,
		Dotted:     n.Dotted,
		Velocity:   n.Velocity,
		fraction:   n.fraction,
		duration:   n.duration,
	}
}

// Replaced is part of Replaceable
func (n Note) Replaced(from, to Sequenceable) Sequenceable {
	if IsIdenticalTo(from, n) {
		return to
	}
	return n
}

var (
	Rest4        = Note{Name: "=", fraction: 0.25}
	PedalUpDown  = Note{Name: "^", fraction: 0}
	PedalDown    = Note{Name: ">", fraction: 0}
	PedalUp      = Note{Name: "<", fraction: 0}
	ZeroDuration = time.Duration(0)
)

func NewNote(name string, octave int, frac float32, accidental int, dot bool, velocity int) (Note, error) {
	if len(name) != 1 {
		return Rest4, fmt.Errorf("note must be one character, got [%s]", name)
	}
	// pedal check
	switch name {
	case "^":
		return PedalUpDown, nil
	case ">":
		return PedalDown, nil
	case "<":
		return PedalUp, nil
	}

	if !strings.Contains(allowedNoteNames, name) {
		return Rest4, fmt.Errorf("invalid note name [%s]:%s", allowedNoteNames, name)
	}
	switch frac {
	case 0.0625:
	case 0.125:
	case 0.25:
	case 0.5:
	case 1:
	default:
		return Rest4, fmt.Errorf("invalid fraction [1,0.5,0.25,0.125,0.0625]:%v", frac)
	}

	if accidental != 0 && accidental != -1 && accidental != 1 {
		return Rest4, fmt.Errorf("invalid accidental: %d", accidental)
	}
	return Note{Name: name, Octave: octave, fraction: frac, Accidental: accidental, Dotted: dot, Velocity: velocity}, nil
}

func MakeNote(name string, octave int, frac float32, accidental int, dot bool, velocity int) Note {
	return Note{Name: name, Octave: octave, fraction: frac, Accidental: accidental, Dotted: dot, Velocity: velocity}
}

func (n Note) IsRest() bool        { return Rest4.Name == n.Name }
func (n Note) IsPedalUp() bool     { return PedalUp.Name == n.Name }
func (n Note) IsPedalDown() bool   { return PedalDown.Name == n.Name }
func (n Note) IsPedalUpDown() bool { return PedalUpDown.Name == n.Name }
func (n Note) IsPedal() bool {
	return PedalUpDown.Name == n.Name || PedalDown.Name == n.Name || PedalUp.Name == n.Name
}

// DurationFactor is the actual duration time factor
func (n Note) DurationFactor() float32 {
	if n.Dotted {
		return n.fraction * 1.5
	}
	return n.fraction
}

func (n Note) S() Sequence {
	return BuildSequence([]Note{n})
}

func (n Note) WithDynamic(emphasis string) Note {
	n.Velocity = ParseVelocity(emphasis)
	return n
}

func (n Note) WithoutDynamic() Note {
	n.Velocity = Normal
	return n
}

func (n Note) WithVelocity(v int) Note {
	n.Velocity = v
	return n
}

func (n Note) WithFraction(f float32, dotted bool) Note {
	// TODO
	if f == 0.5*1.5 {
		n.fraction = 0.5
		n.Dotted = true
		return n
	}
	if f == 0.25*1.5 {
		n.fraction = 0.25
		n.Dotted = true
		return n
	}
	if f == 0.125*1.5 {
		n.fraction = 0.125
		n.Dotted = true
		return n
	}
	if f == 0.0625*1.5 {
		n.fraction = 0.0625
		n.Dotted = true
		return n
	}
	n.fraction = f
	n.Dotted = dotted
	return n
}

func (n Note) IsHearable() bool {
	return strings.ContainsAny(n.Name, "ABCDEFG")
}

// MustParseNote returns a Note by parsing the input. Panic if it fails.
func MustParseNote(input string) Note {
	n, err := ParseNote(input)
	if err != nil {
		panic("MustParseNote failed:" + err.Error())
	}
	return n
}

var N = MustParseNote

// ParseNote reads the format  <(inverse-)duration?>[CDEFGA=<^>]<accidental?><dot?><octave?>
func ParseNote(input string) (Note, error) {
	return newFormatParser(input).parseNote()
}

func ParseVelocity(plusmin string) (velocity int) {
	switch plusmin {
	case "--":
		velocity = VelocityP
	case "---":
		velocity = VelocityPP
	case "----":
		velocity = VelocityPPP
	case "-----":
		velocity = VelocityPPPP
	case "++":
		velocity = VelocityF
	case "+++":
		velocity = VelocityFF
	case "++++":
		velocity = VelocityFFF
	case "+++++":
		velocity = VelocityFFFF
	case "o":
		velocity = Normal
	case "-":
		velocity = VelocityMP
	case "+":
		velocity = VelocityMF
	default:
		// invalid
		velocity = -1
	}
	return
}

// Formatting

func (n Note) accidentalf(encoded bool) string {
	if n.Accidental == -1 {
		if encoded {
			return "b"
		} else {
			return "_"
		}
	}
	if n.Accidental == 1 {
		if encoded {
			return "#"
		} else {
			return "#"
		}
	}
	return ""
}

func (n Note) NonFractionBasedDuration() (time.Duration, bool) {
	if n.duration > 0 {
		return n.duration, true
	}
	return ZeroDuration, false
}

func (n Note) durationf() string {
	switch n.fraction {
	case 0.0625:
		return "16"
	case 0.125:
		return "8"
	case 0.25:
		return "4"
	case 0.5:
		return "2"
	case 1.0:
		return "1"
	}
	return ""
}

func (n Note) Inspect(i Inspection) {
	i.Properties["length"] = n.DurationFactor()
	i.Properties["midi"] = n.MIDI()
	i.Properties["velocity"] = n.Velocity
	wholeNoteDuration := WholeNoteDuration(i.Context.Control().BPM())
	i.Properties["duration"] = time.Duration(float32(wholeNoteDuration) * n.DurationFactor())
}

func (n Note) String() string {
	var buf bytes.Buffer
	n.printOn(&buf, PrintAsSpecified)
	return buf.String()
}

func (n Note) printOn(buf *bytes.Buffer, sharpOrFlatKey int) {
	if n.IsPedalUp() {
		buf.WriteString(PedalUp.Name)
		return
	}
	if n.IsPedalDown() {
		buf.WriteString(PedalDown.Name)
		return
	}
	if n.IsPedalUpDown() {
		buf.WriteString(PedalUpDown.Name)
		return
	}

	if n.fraction != 0.25 {
		buf.WriteString(n.durationf())
	}

	if n.Dotted {
		buf.WriteString(".")
	}

	if n.IsRest() {
		buf.WriteString(n.Name)
		return
	}
	if Sharp == sharpOrFlatKey && n.Accidental == -1 { // want Sharp, specified in Flat
		buf.WriteString(n.Pitched(-1).Name)
		buf.WriteString("#")
	} else if Flat == sharpOrFlatKey && n.Accidental == 1 { // want Flat, specified in Sharp
		buf.WriteString(n.Pitched(1).Name)
		buf.WriteString("_")
	} else { // PrintAsSpecified
		buf.WriteString(n.Name)
		if n.Accidental != 0 {
			buf.WriteString(n.accidentalf(false))
		}
	}
	if n.Octave != 4 {
		fmt.Fprintf(buf, "%d", n.Octave)
	}
	if n.Velocity != Normal {
		io.WriteString(buf, VelocityToDynamic(n.Velocity))
	}
}

func VelocityToDynamic(v int) string {
	if v == Normal {
		return ""
	}
	switch {
	case v <= VelocityPPPP:
		return "-----"
	case v <= VelocityPPP:
		return "----"
	case v <= VelocityPP:
		return "---"
	case v <= VelocityP:
		return "--"
	case v <= VelocityMP:
		return "-"
	case v <= Normal:
	case v <= VelocityMF:
		return "+"
	case v <= VelocityF:
		return "++"
	case v <= VelocityFF:
		return "+++"
	case v <= VelocityFFF:
		return "++++"
	case v > VelocityFFF:
		return "+++++"
	}
	return "?"
}

var fractionRanges = []struct {
	fraction float32
	dotted   bool
}{
	{0.0625, false}, // 1/16
	{0.09375, true},
	{0.125, false},
	{0.1875, true},
	{0.25, false},
	{0.375, true},
	{0.5, false},
	{0.75, true},
	{1.0, false},
	{1.5, true},
	{2.0, false}, // non-exist
}

func QuantizeFraction(durationFactor float32) (fraction float32, dotted bool, ok bool) {
	last := float32(0.0)
	for i := 0; i < len(fractionRanges); i++ {
		next := fractionRanges[i]
		halfway := (last + next.fraction) / 2.0
		if durationFactor <= halfway {
			if i == 0 {
				return 0.0, false, false
			}
			prev := fractionRanges[i-1]
			return prev.fraction, prev.dotted, true
		}
		last = next.fraction
	}
	return 0.0, false, false
}
<|endoftext|>


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

// Package testenv provides information about what functionality
// is available in different testing environments run by the Go team.
//
// It is an internal package because these details are specific
// to the Go team's test setup (on build.golang.org) and not
// fundamental to tests in general.
package testenv

import (
	"errors"
	"flag"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"testing"
)

// Builder reports the name of the builder running this test
// (for example, "linux-amd64" or "windows-386-gce").
// If the test is not running on the build infrastructure,
// Builder returns the empty string.
func Builder() string {
	return os.Getenv("GO_BUILDER_NAME")
}

// HasGoBuild reports whether the current system can build programs with ``go build''
// and then run them with os.StartProcess or exec.Command.
func HasGoBuild() bool {
	switch runtime.GOOS {
	case "android", "nacl":
		return false
	case "darwin":
		if strings.HasPrefix(runtime.GOARCH, "arm") {
			return false
		}
	}
	// gccgo tests can not run "go build".
	return false
}

// MustHaveGoBuild checks that the current system can build programs with ``go build''
// and then run them with os.StartProcess or exec.Command.
// If not, MustHaveGoBuild calls t.Skip with an explanation.
func MustHaveGoBuild(t *testing.T) {
	t.Skip("skipping test: 'go build' not available for gccgo tests")
	if !HasGoBuild() {
		t.Skipf("skipping test: 'go build' not available on %s/%s", runtime.GOOS, runtime.GOARCH)
	}
}

// HasGoRun reports whether the current system can run programs with ``go run.''
func HasGoRun() bool {
	// For now, having go run and having go build are the same.
	return HasGoBuild()
}

// MustHaveGoRun checks that the current system can run programs with ``go run.''
// If not, MustHaveGoRun calls t.Skip with an explanation.
func MustHaveGoRun(t *testing.T) {
	t.Skip("skipping test: 'go run' not available for gccgo tests")
	if !HasGoRun() {
		t.Skipf("skipping test: 'go run' not available on %s/%s", runtime.GOOS, runtime.GOARCH)
	}
}

// GoToolPath reports the path to the Go tool.
// It is a convenience wrapper around GoTool.
// If the tool is unavailable GoToolPath calls t.Skip.
// If the tool should be available and isn't, GoToolPath calls t.Fatal.
func GoToolPath(t *testing.T) string {
	MustHaveGoBuild(t)
	path, err := GoTool()
	if err != nil {
		t.Fatal(err)
	}
	return path
}

// GoTool reports the path to the Go tool.
func GoTool() (string, error) {
	if !HasGoBuild() {
		return "", errors.New("platform cannot run go tool")
	}
	var exeSuffix string
	if runtime.GOOS == "windows" {
		exeSuffix = ".exe"
	}
	path := filepath.Join(runtime.GOROOT(), "bin", "go"+exeSuffix)
	if _, err := os.Stat(path); err == nil {
		return path, nil
	}
	goBin, err := exec.LookPath("go" + exeSuffix)
	if err != nil {
		return "", errors.New("cannot find go tool: " + err.Error())
	}
	return goBin, nil
}

// HasExec reports whether the current system can start new processes
// using os.StartProcess or (more commonly) exec.Command.
func HasExec() bool {
	switch runtime.GOOS {
	case "nacl":
		return false
	case "darwin":
		if strings.HasPrefix(runtime.GOARCH, "arm") {
			return false
		}
	}
	return true
}

// MustHaveExec checks that the current system can start new processes
// using os.StartProcess or (more commonly) exec.Command.
// If not, MustHaveExec calls t.Skip with an explanation.
func MustHaveExec(t *testing.T) {
	if !HasExec() {
		t.Skipf("skipping test: cannot exec subprocess on %s/%s", runtime.GOOS, runtime.GOARCH)
	}
}

// HasExternalNetwork reports whether the current system can use
// external (non-localhost) networks.
func HasExternalNetwork() bool {
	return !testing.Short()
}

// MustHaveExternalNetwork checks that the current system can use
// external (non-localhost) networks.
// If not, MustHaveExternalNetwork calls t.Skip with an explanation.
func MustHaveExternalNetwork(t *testing.T) {
	if testing.Short() {
		t.Skipf("skipping test: no external network in -short mode")
	}
}

var haveCGO bool

// MustHaveCGO calls t.Skip if cgo is not available.
func MustHaveCGO(t *testing.T) {
	if !haveCGO {
		t.Skipf("skipping test: no cgo")
	}
}

// HasSymlink reports whether the current system can use os.Symlink.
func HasSymlink() bool {
	ok, _ := hasSymlink()
	return ok
}

// MustHaveSymlink reports whether the current system can use os.Symlink.
// If not, MustHaveSymlink calls t.Skip with an explanation.
func MustHaveSymlink(t *testing.T) {
	ok, reason := hasSymlink()
	if !ok {
		t.Skipf("skipping test: cannot make symlinks on %s/%s%s", runtime.GOOS, runtime.GOARCH, reason)
	}
}

// HasLink reports whether the current system can use os.Link.
func HasLink() bool {
	// From Android release M (Marshmallow), hard linking files is blocked
	// and an attempt to call link() on a file will return EACCES.
	// - https://code.google.com/p/android-developer-preview/issues/detail?id=3150
	return runtime.GOOS != "plan9" && runtime.GOOS != "android"
}

// MustHaveLink reports whether the current system can use os.Link.
// If not, MustHaveLink calls t.Skip with an explanation.
func MustHaveLink(t *testing.T) {
	if !HasLink() {
		t.Skipf("skipping test: hardlinks are not supported on %s/%s", runtime.GOOS, runtime.GOARCH)
	}
}

var flaky = flag.Bool("flaky", false, "run known-flaky tests too")

func SkipFlaky(t *testing.T, issue int) {
	if !*flaky {
		t.Skipf("skipping known flaky test without the -flaky flag; see golang.org/issue/%d", issue)
	}
}

func SkipFlakyNet(t *testing.T) {
	if v, _ := strconv.ParseBool(os.Getenv("GO_BUILDER_FLAKY_NET")); v {
		t.Skip("skipping test on builder known to have frequent network failures")
	}
}
<commit_msg>internal/testenv: support testing gccgo<commit_after>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package testenv provides information about what functionality
// is available in different testing environments run by the Go team.
//
// It is an internal package because these details are specific
// to the Go team's test setup (on build.golang.org) and not
// fundamental to tests in general.
package testenv

import (
	"errors"
	"flag"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"testing"
)

// testingGotools reports whether we are testing the gotools directory
// that is part of GCC. We just use an environment variable set by the
// gotools check target.
func testingGotools() bool {
	return os.Getenv("GO_TESTING_GOTOOLS") != ""
}

// Builder reports the name of the builder running this test
// (for example, "linux-amd64" or "windows-386-gce").
// If the test is not running on the build infrastructure,
// Builder returns the empty string.
func Builder() string {
	return os.Getenv("GO_BUILDER_NAME")
}

// HasGoBuild reports whether the current system can build programs with ``go build''
// and then run them with os.StartProcess or exec.Command.
func HasGoBuild() bool {
	switch runtime.GOOS {
	case "android", "nacl":
		return false
	case "darwin":
		if strings.HasPrefix(runtime.GOARCH, "arm") {
			return false
		}
	}
	// gccgo tests can not run "go build".
	return testingGotools()
}

// MustHaveGoBuild checks that the current system can build programs with ``go build''
// and then run them with os.StartProcess or exec.Command.
// If not, MustHaveGoBuild calls t.Skip with an explanation.
func MustHaveGoBuild(t *testing.T) {
	if !testingGotools() {
		t.Skip("skipping test: 'go build' not available for gccgo tests")
	}
	if !HasGoBuild() {
		t.Skipf("skipping test: 'go build' not available on %s/%s", runtime.GOOS, runtime.GOARCH)
	}
}

// HasGoRun reports whether the current system can run programs with ``go run.''
func HasGoRun() bool {
	// For now, having go run and having go build are the same.
	return HasGoBuild()
}

// MustHaveGoRun checks that the current system can run programs with ``go run.''
// If not, MustHaveGoRun calls t.Skip with an explanation.
func MustHaveGoRun(t *testing.T) {
	if !testingGotools() {
		t.Skip("skipping test: 'go run' not available for gccgo tests")
	}
	if !HasGoRun() {
		t.Skipf("skipping test: 'go run' not available on %s/%s", runtime.GOOS, runtime.GOARCH)
	}
}

// GoToolPath reports the path to the Go tool.
// It is a convenience wrapper around GoTool.
// If the tool is unavailable GoToolPath calls t.Skip.
// If the tool should be available and isn't, GoToolPath calls t.Fatal.
func GoToolPath(t *testing.T) string {
	MustHaveGoBuild(t)
	path, err := GoTool()
	if err != nil {
		t.Fatal(err)
	}
	return path
}

// GoTool reports the path to the Go tool.
func GoTool() (string, error) {
	if !HasGoBuild() {
		return "", errors.New("platform cannot run go tool")
	}
	var exeSuffix string
	if runtime.GOOS == "windows" {
		exeSuffix = ".exe"
	}
	path := filepath.Join(runtime.GOROOT(), "bin", "go"+exeSuffix)
	if _, err := os.Stat(path); err == nil {
		return path, nil
	}
	goBin, err := exec.LookPath("go" + exeSuffix)
	if err != nil {
		return "", errors.New("cannot find go tool: " + err.Error())
	}
	return goBin, nil
}

// HasExec reports whether the current system can start new processes
// using os.StartProcess or (more commonly) exec.Command.
func HasExec() bool {
	switch runtime.GOOS {
	case "nacl":
		return false
	case "darwin":
		if strings.HasPrefix(runtime.GOARCH, "arm") {
			return false
		}
	}
	return true
}

// MustHaveExec checks that the current system can start new processes
// using os.StartProcess or (more commonly) exec.Command.
// If not, MustHaveExec calls t.Skip with an explanation.
func MustHaveExec(t *testing.T) {
	if !HasExec() {
		t.Skipf("skipping test: cannot exec subprocess on %s/%s", runtime.GOOS, runtime.GOARCH)
	}
}

// HasExternalNetwork reports whether the current system can use
// external (non-localhost) networks.
func HasExternalNetwork() bool {
	return !testing.Short()
}

// MustHaveExternalNetwork checks that the current system can use
// external (non-localhost) networks.
// If not, MustHaveExternalNetwork calls t.Skip with an explanation.
func MustHaveExternalNetwork(t *testing.T) {
	if testing.Short() {
		t.Skipf("skipping test: no external network in -short mode")
	}
}

var haveCGO bool

// MustHaveCGO calls t.Skip if cgo is not available.
func MustHaveCGO(t *testing.T) {
	if !haveCGO {
		t.Skipf("skipping test: no cgo")
	}
}

// HasSymlink reports whether the current system can use os.Symlink.
func HasSymlink() bool {
	ok, _ := hasSymlink()
	return ok
}

// MustHaveSymlink reports whether the current system can use os.Symlink.
// If not, MustHaveSymlink calls t.Skip with an explanation.
func MustHaveSymlink(t *testing.T) {
	ok, reason := hasSymlink()
	if !ok {
		t.Skipf("skipping test: cannot make symlinks on %s/%s%s", runtime.GOOS, runtime.GOARCH, reason)
	}
}

// HasLink reports whether the current system can use os.Link.
func HasLink() bool {
	// From Android release M (Marshmallow), hard linking files is blocked
	// and an attempt to call link() on a file will return EACCES.
	// - https://code.google.com/p/android-developer-preview/issues/detail?id=3150
	return runtime.GOOS != "plan9" && runtime.GOOS != "android"
}

// MustHaveLink reports whether the current system can use os.Link.
// If not, MustHaveLink calls t.Skip with an explanation.
func MustHaveLink(t *testing.T) {
	if !HasLink() {
		t.Skipf("skipping test: hardlinks are not supported on %s/%s", runtime.GOOS, runtime.GOARCH)
	}
}

var flaky = flag.Bool("flaky", false, "run known-flaky tests too")

func SkipFlaky(t *testing.T, issue int) {
	if !*flaky {
		t.Skipf("skipping known flaky test without the -flaky flag; see golang.org/issue/%d", issue)
	}
}

func SkipFlakyNet(t *testing.T) {
	if v, _ := strconv.ParseBool(os.Getenv("GO_BUILDER_FLAKY_NET")); v {
		t.Skip("skipping test on builder known to have frequent network failures")
	}
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
)

type row []int
type matrix []row

// check returns an error if matrix is irregular
// and cannot be transposed.
func (m matrix) check() (err error) {
	l := len(m[0])
	for _, x := range m {
		// as long as first line is the longest we're fine
		if len(x) > l {
			return errors.New("first lane is not longest")
		}
	}
	return nil
}

func (m matrix) transpose() matrix {
	r := make(matrix, len(m[0]))
	for x := range r {
		r[x] = make(row, len(m))
	}
	for y, s := range m {
		for x, e := range s {
			r[x][y] = e
		}
	}
	return r
}
<commit_msg>Add row equality method, at least needed for tests<commit_after>package main

import (
	"errors"
)

type row []int
type matrix []row

func (r1 row) eq(r2 row) bool {
	if len(r1) != len(r2) {
		return false
	}
	for i := range r1 {
		if r1[i] != r2[i] {
			return false
		}
	}
	return true
}

// check returns an error if matrix is irregular
// and cannot be transposed.
func (m matrix) check() (err error) {
	l := len(m[0])
	for _, x := range m {
		// as long as first line is the longest we're fine
		if len(x) > l {
			return errors.New("first lane is not longest")
		}
	}
	return nil
}

func (m matrix) transpose() matrix {
	r := make(matrix, len(m[0]))
	for x := range r {
		r[x] = make(row, len(m))
	}
	for y, s := range m {
		for x, e := range s {
			r[x][y] = e
		}
	}
	return r
}
<|endoftext|>


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

// Package label contains common definitions for workload labels used by Istio.
package label

const (
	// TLSMode is the name of label given to service instances to determine whether to use mTLS or
	// fallback to plaintext/tls
	TLSMode = "security.istio.io/tlsMode"

	// IstioCanonicalServiceName is the name of label for the Istio Canonical Service for a workload instance.
	IstioCanonicalServiceName = "service.istio.io/canonical-name"

	// IstioCanonicalServiceRevision is the name of label for the Istio Canonical Service revision for a workload instance.
	IstioCanonicalServiceRevision = "service.istio.io/canonical-revision"

	// IstioRev is the Istio control plane revision associated with the resource; e.g. "canary"
	IstioRev = "istio.io/rev"

	// IstioOperatorComponent is the Istio operator component name of the resource, e.g. "Pilot"
	IstioOperatorComponent = "operator.istio.io/component"

	// IstioOperatorManaged is "Reconcile" if the Istio operator will reconcile the resource.
	IstioOperatorManaged = "operator.istio.io/managed"

	// IstioOperatorVersion is the Istio operator version that installed the resource, e.g. "1.6.0"
	IstioOperatorVersion = "operator.istio.io/version"

	// IstioNetwork enables Istio to group endpoints resident in the same L3 domain/network.
	// All endpoints in the same network are assumed to be directly reachable from one another.
	// When endpoints in different networks cannot reach each other directly, an Istio Gateway can
	// be used to establish connectivity (usually using the AUTO_PASSTHROUGH mode in a  Gateway Server).
	// This is an advanced configuration used typically for spanning an Istio mesh over multiple clusters.
	IstioNetwork = "topology.istio.io/network"

	// IstioCluster is a workload label that indicates the name of the cluster that contains the
	// workload. The label is added internally within the control plane to enable workload selection
	// based on cluster.
	IstioCluster = "topology.istio.io/cluster"
)
<commit_msg>Add labels for locality (#1690)<commit_after>// Copyright 2020 Istio Authors
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

// Package label contains common definitions for workload labels used by Istio.
package label

const (
	// TLSMode is the name of label given to service instances to determine whether to use mTLS or
	// fallback to plaintext/tls
	TLSMode = "security.istio.io/tlsMode"

	// IstioCanonicalServiceName is the name of label for the Istio Canonical Service for a workload instance.
	IstioCanonicalServiceName = "service.istio.io/canonical-name"

	// IstioCanonicalServiceRevision is the name of label for the Istio Canonical Service revision for a workload instance.
	IstioCanonicalServiceRevision = "service.istio.io/canonical-revision"

	// IstioRev is the Istio control plane revision associated with the resource; e.g. "canary"
	IstioRev = "istio.io/rev"

	// IstioOperatorComponent is the Istio operator component name of the resource, e.g. "Pilot"
	IstioOperatorComponent = "operator.istio.io/component"

	// IstioOperatorManaged is "Reconcile" if the Istio operator will reconcile the resource.
	IstioOperatorManaged = "operator.istio.io/managed"

	// IstioOperatorVersion is the Istio operator version that installed the resource, e.g. "1.6.0"
	IstioOperatorVersion = "operator.istio.io/version"

	// IstioSubZone identifies the locality subzone of a workload.
	IstioSubZone = "topology.istio.io/subzone"

	// IstioNetwork enables Istio to group endpoints resident in the same L3 domain/network.
	// All endpoints in the same network are assumed to be directly reachable from one another.
	// When endpoints in different networks cannot reach each other directly, an Istio Gateway can
	// be used to establish connectivity (usually using the AUTO_PASSTHROUGH mode in a  Gateway Server).
	// This is an advanced configuration used typically for spanning an Istio mesh over multiple clusters.
	IstioNetwork = "topology.istio.io/network"

	// IstioCluster is a workload label that indicates the name of the cluster that contains the
	// workload. The label is added internally within the control plane to enable workload selection
	// based on cluster.
	IstioCluster = "topology.istio.io/cluster"
)
<|endoftext|>