<commit_before>// Package fileref provides a unified representation of 64-bit and 128-bit
// file identifiers used in NTFS and ReFS file systems.
//
// New identifiers are created by calling New64, New128, BigEndian or
// LittleEndian.
package fileref

import (
	"math/big"
	"strconv"
)

// ID is a file identifer capable of holding 64-bit and 128-bit values.
//
// ID is stored as a big-endian byte sequence. NTFS and ReFS use a little-endian
// byte sequence, so the LittleEndian function should be used to create
// identifiers for byte sequences taken from those file systems.
type ID [16]byte

// New64 returns a 64-bit file identifier.
func New64(value int64) (id ID) {
	return New128(value, 0)
}

// New128 returns a 128-bit file identifier for a set of lower and upper
// 64 bit values.
func New128(lower int64, upper int64) (id ID) {
	id[0] = byte(upper >> 56)
	id[1] = byte(upper >> 48)
	id[2] = byte(upper >> 40)
	id[3] = byte(upper >> 32)
	id[4] = byte(upper >> 24)
	id[5] = byte(upper >> 16)
	id[6] = byte(upper >> 8)
	id[7] = byte(upper)
	id[8] = byte(lower >> 56)
	id[9] = byte(lower >> 48)
	id[10] = byte(lower >> 40)
	id[11] = byte(lower >> 32)
	id[12] = byte(lower >> 24)
	id[13] = byte(lower >> 16)
	id[14] = byte(lower >> 8)
	id[15] = byte(lower)
	return
}

// BigEndian creates a file identifier from a sequence of bytes in big-endian
// byte order.
func BigEndian(value [16]byte) ID {
	return ID(value)
}

// LittleEndian creates a file identifier from a sequence of bytes in
// little-endian byte order.
func LittleEndian(value [16]byte) ID {
	for i, j := 0, 15; i < j; i, j = i+1, j-1 {
		value[i], value[j] = value[j], value[i]
	}
	return ID(value)
}

// Int64 returns the file identifier as a 64-bit signed integer if it can be
// represented as one. If it cannot, -1 is returned.
func (id ID) Int64() int64 {
	upper, lower := id.Split()
	if upper != 0 {
		return -1
	}
	return lower
}

// IsInt64 returns true if the file identifier can be represented as a 64-bit
// signed integer.
func (id ID) IsInt64() bool {
	upper := int64(id[0])<<56 | int64(id[1])<<48 | int64(id[2])<<40 | int64(id[3])<<32 | int64(id[4])<<24 | int64(id[5])<<16 | int64(id[6])<<8 | int64(id[7])
	return upper == 0
}

// IsZero returns true if the file identifier is zero.
func (id ID) IsZero() bool {
	upper, lower := id.Split()
	return upper == 0 && lower == 0
}

// BigEndian returns the ID as a sequence of bytes in big-endian byte order.
func (id ID) BigEndian() (value [16]byte) {
	return [16]byte(id)
}

// LittleEndian returns the ID as a sequence of bytes in little-endian byte
// order.
func (id ID) LittleEndian() (value [16]byte) {
	for i := 0; i < 16; i++ {
		value[i] = id[15-i]
	}
	return
}

// Descriptor returns a descriptor for the file id.
func (id ID) Descriptor() Descriptor {
	if id.IsInt64() {
		return Descriptor{
			Size: 16,
			Type: FileType,
			Data: id.LittleEndian(),
		}
	}
	return Descriptor{
		Size: 24,
		Type: ExtendedFileIDType,
		Data: id.LittleEndian(),
	}
}

// String returns a string representation of the file identifier.
func (id ID) String() string {
	if id.IsInt64() {
		return strconv.FormatInt(id.Int64(), 10)
	}
	bi := big.NewInt(0)
	bi.SetBytes(id[:]) // This assumes that id is in big-endian byte order and that it is unsigned
	return bi.String()
}

// Split breaks the ID into upper and lower 64-bit values.
func (id ID) Split() (upper, lower int64) {
	upper = int64(id[0])<<56 | int64(id[1])<<48 | int64(id[2])<<40 | int64(id[3])<<32 | int64(id[4])<<24 | int64(id[5])<<16 | int64(id[6])<<8 | int64(id[7])
	lower = int64(id[8])<<56 | int64(id[9])<<48 | int64(id[10])<<40 | int64(id[11])<<32 | int64(id[12])<<24 | int64(id[13])<<16 | int64(id[14])<<8 | int64(id[15])
	return
}
<commit_msg>Fix descriptor size to 24<commit_after>// Package fileref provides a unified representation of 64-bit and 128-bit
// file identifiers used in NTFS and ReFS file systems.
//
// New identifiers are created by calling New64, New128, BigEndian or
// LittleEndian.
package fileref

import (
	"math/big"
	"strconv"
)

// ID is a file identifer capable of holding 64-bit and 128-bit values.
//
// ID is stored as a big-endian byte sequence. NTFS and ReFS use a little-endian
// byte sequence, so the LittleEndian function should be used to create
// identifiers for byte sequences taken from those file systems.
type ID [16]byte

// New64 returns a 64-bit file identifier.
func New64(value int64) (id ID) {
	return New128(value, 0)
}

// New128 returns a 128-bit file identifier for a set of lower and upper
// 64 bit values.
func New128(lower int64, upper int64) (id ID) {
	id[0] = byte(upper >> 56)
	id[1] = byte(upper >> 48)
	id[2] = byte(upper >> 40)
	id[3] = byte(upper >> 32)
	id[4] = byte(upper >> 24)
	id[5] = byte(upper >> 16)
	id[6] = byte(upper >> 8)
	id[7] = byte(upper)
	id[8] = byte(lower >> 56)
	id[9] = byte(lower >> 48)
	id[10] = byte(lower >> 40)
	id[11] = byte(lower >> 32)
	id[12] = byte(lower >> 24)
	id[13] = byte(lower >> 16)
	id[14] = byte(lower >> 8)
	id[15] = byte(lower)
	return
}

// BigEndian creates a file identifier from a sequence of bytes in big-endian
// byte order.
func BigEndian(value [16]byte) ID {
	return ID(value)
}

// LittleEndian creates a file identifier from a sequence of bytes in
// little-endian byte order.
func LittleEndian(value [16]byte) ID {
	for i, j := 0, 15; i < j; i, j = i+1, j-1 {
		value[i], value[j] = value[j], value[i]
	}
	return ID(value)
}

// Int64 returns the file identifier as a 64-bit signed integer if it can be
// represented as one. If it cannot, -1 is returned.
func (id ID) Int64() int64 {
	upper, lower := id.Split()
	if upper != 0 {
		return -1
	}
	return lower
}

// IsInt64 returns true if the file identifier can be represented as a 64-bit
// signed integer.
func (id ID) IsInt64() bool {
	upper := int64(id[0])<<56 | int64(id[1])<<48 | int64(id[2])<<40 | int64(id[3])<<32 | int64(id[4])<<24 | int64(id[5])<<16 | int64(id[6])<<8 | int64(id[7])
	return upper == 0
}

// IsZero returns true if the file identifier is zero.
func (id ID) IsZero() bool {
	upper, lower := id.Split()
	return upper == 0 && lower == 0
}

// BigEndian returns the ID as a sequence of bytes in big-endian byte order.
func (id ID) BigEndian() (value [16]byte) {
	return [16]byte(id)
}

// LittleEndian returns the ID as a sequence of bytes in little-endian byte
// order.
func (id ID) LittleEndian() (value [16]byte) {
	for i := 0; i < 16; i++ {
		value[i] = id[15-i]
	}
	return
}

// Descriptor returns a descriptor for the file id.
func (id ID) Descriptor() Descriptor {
	if id.IsInt64() {
		return Descriptor{
			Size: 24,
			Type: FileType,
			Data: id.LittleEndian(),
		}
	}
	return Descriptor{
		Size: 24,
		Type: ExtendedFileIDType,
		Data: id.LittleEndian(),
	}
}

// String returns a string representation of the file identifier.
func (id ID) String() string {
	if id.IsInt64() {
		return strconv.FormatInt(id.Int64(), 10)
	}
	bi := big.NewInt(0)
	bi.SetBytes(id[:]) // This assumes that id is in big-endian byte order and that it is unsigned
	return bi.String()
}

// Split breaks the ID into upper and lower 64-bit values.
func (id ID) Split() (upper, lower int64) {
	upper = int64(id[0])<<56 | int64(id[1])<<48 | int64(id[2])<<40 | int64(id[3])<<32 | int64(id[4])<<24 | int64(id[5])<<16 | int64(id[6])<<8 | int64(id[7])
	lower = int64(id[8])<<56 | int64(id[9])<<48 | int64(id[10])<<40 | int64(id[11])<<32 | int64(id[12])<<24 | int64(id[13])<<16 | int64(id[14])<<8 | int64(id[15])
	return
}
<|endoftext|>


<commit_before>package ai

import (
	"log"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/hysios/apiai-go"
)

const (
	MODE_PRODUCTS      = 1
	MODE_PRODUCT_ITEMS = 2
	MODE_PROD_TASTES   = 3
)

type ApiAiOrder struct {
	AiResult apiai.Result
}

func (aa ApiAiOrder) Mode() int {
	mode := MODE_PRODUCTS

	if _, ok := aa.AiResult.Params["productItems"]; ok {
		mode = MODE_PRODUCT_ITEMS
	}

	if _, ok := aa.AiResult.Params["prodTasts"]; ok {
		mode = MODE_PROD_TASTES
	}

	return mode
}

func (aa ApiAiOrder) Score() float64 {
	return aa.AiResult.Score
}

func (aa ApiAiOrder) Query() string {
	return aa.AiResult.ResolvedQuery
}

func (aa ApiAiOrder) Items() []Item {
	if aa.Mode() == MODE_PRODUCT_ITEMS {
		return aa.ExtractProductItems("productItems")
	} else if aa.Mode() == MODE_PROD_TASTES {
		products := aa.prodTastItems()
		quantities := aa.Quantities()

		return composeItems(products, quantities)
	} else {
		products := aa.Products()
		quantities := aa.Quantities()

		return composeItems(products, quantities)
	}

	return make([]Item, 0)

	// for i, q := range quantities {
	// 	if len(products) >= i+1 {
	// 		products[i].Quantity = q.Quantity
	// 		products[i].Unit = q.Unit
	// 	}
	// }

	// return products
}

func (aa ApiAiOrder) Products() []Item {
	return aa.ExtractProducts("products")
}

func (aa ApiAiOrder) prodTastItems() []Item {
	return aa.ExtractProducts("prodTasts")
}

func (aa ApiAiOrder) Quantities() []Item {
	return aa.ExtractQuantities("quantity")
}

func (aa ApiAiOrder) GiftItems() []Item {
	if aa.Mode() == MODE_PRODUCT_ITEMS {
		return aa.ExtractProductItems("giftItems")
	}

	gifts := aa.GiftProducts()
	quantities := aa.GiftQuantities()

	return composeItems(gifts, quantities)

	// for i, q := range quantities {
	// 	if len(gifts) >= i+1 {
	// 		gifts[i].Quantity = q.Quantity
	// 		gifts[i].Unit = q.Unit
	// 	}
	// }

	// return gifts
}

func composeItems(products []Item, quantities []Item) []Item {
	result := make([]Item, 0, 0)
	l := len(products)
	qlen := len(quantities)

	if l < qlen {
		l = qlen
	}

	for i := 0; i < l; i++ {
		item := Item{}

		if len(products) >= i+1 {
			p := products[i]
			item.Product = p.Product

			if p.Taste != "" {
				item.Taste = p.Taste
			}
		}

		if len(quantities) >= i+1 {
			q := quantities[i]
			item.Quantity = q.Quantity
			item.Unit = q.Unit
		}

		result = append(result, item)
	}

	return result
}

func (aa ApiAiOrder) GiftProducts() []Item {
	return aa.ExtractProducts("gifts")
}

func (aa ApiAiOrder) GiftQuantities() []Item {
	return aa.ExtractQuantities("giftNumber")
}

func (aa ApiAiOrder) Address() string {
	if a, exist := aa.AiResult.Params["street-address"]; exist {

		rt := reflect.TypeOf(a)
		vals := reflect.ValueOf(a)

		switch rt.Kind() {
		case reflect.Slice:
			if vals.Len() > 0 {
				return vals.Index(0).Interface().(string)
			}
		case reflect.Array:
			if vals.Len() > 0 {
				return vals.Index(0).Interface().(string)
			}
		case reflect.String:
			return vals.Interface().(string)
		}
	}

	return ""
}

func (aa ApiAiOrder) Customer() string {
	if c, exist := aa.AiResult.Params["customer"]; exist {

		rt := reflect.TypeOf(c)
		vals := reflect.ValueOf(c)

		switch rt.Kind() {
		case reflect.Slice:
			if vals.Len() > 0 {
				return vals.Index(0).Interface().(string)
			}
		case reflect.Array:
			if vals.Len() > 0 {
				return vals.Index(0).Interface().(string)
			}
		case reflect.String:
			return vals.Interface().(string)
		}
	}

	return ""
}

func (aa ApiAiOrder) Count() int {
	if c, exist := aa.AiResult.Params["number"]; exist {
		switch c.(type) {
		case float64:
			fval := c.(float64)
			return int(fval)
		case float32:
			fval := c.(float32)
			return int(fval)
		case int:
			return c.(int)
		case string:
			sval := c.(string)
			ival, err := strconv.Atoi(sval)
			if err != nil {
				return 0
			} else {
				return ival
			}
		}
	}

	return 0
}

func (aa ApiAiOrder) Duration() string {
	if c, exist := aa.AiResult.Params["duration"]; exist {
		return c.(string)
	}

	return ""
}

func (aa ApiAiOrder) Time() time.Time {
	if t, exist := aa.AiResult.Params["date"]; exist {
		if aiTime, err := time.Parse("2006-01-02", t.(string)); err == nil {
			return aiTime
		}
	}

	return time.Time{}
}

func (aa ApiAiOrder) Fulfiled() bool {
	return true
}

func (aa ApiAiOrder) Note() string {
	if imp, exist := aa.AiResult.Params["important"]; exist {
		return imp.(string)
	}

	return ""
}

// MODE 1 extracting, by key products
func (aa ApiAiOrder) ExtractProducts(t string) []Item {
	result := make([]Item, 0, 50)

	if products, exist := aa.AiResult.Params[t]; exist {
		ps := reflect.ValueOf(products)

		for i := 0; i < ps.Len(); i++ {
			p := ps.Index(i).Interface()

			switch v := p.(type) {
			case string:
				// name := p.(string)
				item := Item{Product: v}
				result = append(result, item)
			case map[string]interface{}:
				// itemMap := p.(map[string]interface{})
				name, _ := v["product"].(string)
				taste, _ := v["taste"].(string)

				if name != "" {
					item := Item{
						Product: name,
					}

					if taste != "" {
						item.Taste = taste
					}

					result = append(result, item)
				}
			}
		}
	}

	return result
}

// MODE 2 extracting, by key productItems
func (aa ApiAiOrder) ExtractProductItems(s string) []Item {
	result := make([]Item, 0)

	if prodItems, ok := aa.AiResult.Params[s]; ok {
		ps := reflect.ValueOf(prodItems)

		for i := 0; i < ps.Len(); i++ {
			var name, unit, spec, taste string
			var quantity int

			p := ps.Index(i)
			prodItem := p.Interface().(map[string]interface{})
			name, _ = prodItem["product"].(string)
			spec, _ = prodItem["spec"].(string)
			taste, _ = prodItem["taste"].(string)

			quanMap, _ := prodItem["quantity"].(map[string]interface{})
			numberFloat, ok := quanMap["number"].(float64)
			if ok {
				quantity = int(numberFloat)
			}

			unit, _ = quanMap["unit"].(string)

			item := Item{
				Product:  name,
				Quantity: quantity,
				Unit:     unit,
				Spec:     spec,
				Taste:    taste,
			}
			result = append(result, item)
		}
	}

	return result
}

func (aa ApiAiOrder) ExtractQuantities(t string) []Item {
	result := make([]Item, 0, 50)

	if quantities, exist := aa.AiResult.Params[t]; exist {
		qs := reflect.ValueOf(quantities)

		for i := 0; i < qs.Len(); i++ {
			q := qs.Index(i).Interface()

			switch v := q.(type) {
			case string:
				// qs := q.(string)
				qi := extractQuantity(v)
				item := Item{Quantity: qi}
				result = append(result, item)
			case float64:
				// qf := q.(float64)
				item := Item{Quantity: int(v)}
				result = append(result, item)
			case map[string]interface{}:
				// quanMap := q.(map[string]interface{})
				log.Printf("quantity: %v\n", t)
				qf, ok := v["number"].(float64)
				if !ok {
					qf, _ = v["quantity"].(float64)
				}

				quantity := int(qf)
				item := Item{Quantity: quantity}

				if unit, ok := v["unit"].(string); ok {
					unit = strings.Replace(unit, "龘", "", -1)
					unit = strings.Replace(unit, " ", "", -1)
					item.Unit = unit
				}

				result = append(result, item)
			default:
				log.Println("Unknown Quantity type: %v", t)
			}
		}
	}

	return result
}

func extractQuantity(s string) int {
	nums := strings.TrimFunc(s, TrimToNum)
	numsCgd := DBCtoSBC(nums)

	if len(numsCgd) > 0 {
		q, _ := strconv.Atoi(numsCgd)
		return q
	} else {
		return 0
	}
	// re := regexp.MustCompile("[0-9]+")
}

func TrimToNum(r rune) bool {
	if n := r - '0'; n >= 0 && n <= 9 {
		return false
	} else if m := r - '０'; m >= 0 && m <= 9 {
		return false
	}

	return true
}

func DBCtoSBC(s string) string {
	retstr := ""
	for _, i := range s {
		inside_code := i
		if inside_code == 12288 {
			inside_code = 32
		} else {
			inside_code -= 65248
		}
		if inside_code < 32 || inside_code > 126 {
			retstr += string(i)
		} else {
			retstr += string(inside_code)
		}
	}
	return retstr
}
<commit_msg>fix key name<commit_after>package ai

import (
	"log"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/hysios/apiai-go"
)

const (
	MODE_PRODUCTS      = 1
	MODE_PRODUCT_ITEMS = 2
	MODE_PROD_TASTES   = 3
)

type ApiAiOrder struct {
	AiResult apiai.Result
}

func (aa ApiAiOrder) Mode() int {
	mode := MODE_PRODUCTS

	if _, ok := aa.AiResult.Params["productItems"]; ok {
		mode = MODE_PRODUCT_ITEMS
	}

	if _, ok := aa.AiResult.Params["prodTasts"]; ok {
		mode = MODE_PROD_TASTES
	}

	return mode
}

func (aa ApiAiOrder) Score() float64 {
	return aa.AiResult.Score
}

func (aa ApiAiOrder) Query() string {
	return aa.AiResult.ResolvedQuery
}

func (aa ApiAiOrder) Items() []Item {
	if aa.Mode() == MODE_PRODUCT_ITEMS {
		return aa.ExtractProductItems("productItems")
	} else if aa.Mode() == MODE_PROD_TASTES {
		products := aa.prodTastItems()
		quantities := aa.Quantities()

		return composeItems(products, quantities)
	} else {
		products := aa.Products()
		quantities := aa.Quantities()

		return composeItems(products, quantities)
	}

	return make([]Item, 0)

	// for i, q := range quantities {
	// 	if len(products) >= i+1 {
	// 		products[i].Quantity = q.Quantity
	// 		products[i].Unit = q.Unit
	// 	}
	// }

	// return products
}

func (aa ApiAiOrder) Products() []Item {
	return aa.ExtractProducts("products")
}

func (aa ApiAiOrder) prodTastItems() []Item {
	return aa.ExtractProducts("prodTastes")
}

func (aa ApiAiOrder) Quantities() []Item {
	return aa.ExtractQuantities("quantity")
}

func (aa ApiAiOrder) GiftItems() []Item {
	if aa.Mode() == MODE_PRODUCT_ITEMS {
		return aa.ExtractProductItems("giftItems")
	}

	gifts := aa.GiftProducts()
	quantities := aa.GiftQuantities()

	return composeItems(gifts, quantities)

	// for i, q := range quantities {
	// 	if len(gifts) >= i+1 {
	// 		gifts[i].Quantity = q.Quantity
	// 		gifts[i].Unit = q.Unit
	// 	}
	// }

	// return gifts
}

func composeItems(products []Item, quantities []Item) []Item {
	result := make([]Item, 0, 0)
	l := len(products)
	qlen := len(quantities)

	if l < qlen {
		l = qlen
	}

	for i := 0; i < l; i++ {
		item := Item{}

		if len(products) >= i+1 {
			p := products[i]
			item.Product = p.Product

			if p.Taste != "" {
				item.Taste = p.Taste
			}
		}

		if len(quantities) >= i+1 {
			q := quantities[i]
			item.Quantity = q.Quantity
			item.Unit = q.Unit
		}

		result = append(result, item)
	}

	return result
}

func (aa ApiAiOrder) GiftProducts() []Item {
	return aa.ExtractProducts("gifts")
}

func (aa ApiAiOrder) GiftQuantities() []Item {
	return aa.ExtractQuantities("giftNumber")
}

func (aa ApiAiOrder) Address() string {
	if a, exist := aa.AiResult.Params["street-address"]; exist {

		rt := reflect.TypeOf(a)
		vals := reflect.ValueOf(a)

		switch rt.Kind() {
		case reflect.Slice:
			if vals.Len() > 0 {
				return vals.Index(0).Interface().(string)
			}
		case reflect.Array:
			if vals.Len() > 0 {
				return vals.Index(0).Interface().(string)
			}
		case reflect.String:
			return vals.Interface().(string)
		}
	}

	return ""
}

func (aa ApiAiOrder) Customer() string {
	if c, exist := aa.AiResult.Params["customer"]; exist {

		rt := reflect.TypeOf(c)
		vals := reflect.ValueOf(c)

		switch rt.Kind() {
		case reflect.Slice:
			if vals.Len() > 0 {
				return vals.Index(0).Interface().(string)
			}
		case reflect.Array:
			if vals.Len() > 0 {
				return vals.Index(0).Interface().(string)
			}
		case reflect.String:
			return vals.Interface().(string)
		}
	}

	return ""
}

func (aa ApiAiOrder) Count() int {
	if c, exist := aa.AiResult.Params["number"]; exist {
		switch c.(type) {
		case float64:
			fval := c.(float64)
			return int(fval)
		case float32:
			fval := c.(float32)
			return int(fval)
		case int:
			return c.(int)
		case string:
			sval := c.(string)
			ival, err := strconv.Atoi(sval)
			if err != nil {
				return 0
			} else {
				return ival
			}
		}
	}

	return 0
}

func (aa ApiAiOrder) Duration() string {
	if c, exist := aa.AiResult.Params["duration"]; exist {
		return c.(string)
	}

	return ""
}

func (aa ApiAiOrder) Time() time.Time {
	if t, exist := aa.AiResult.Params["date"]; exist {
		if aiTime, err := time.Parse("2006-01-02", t.(string)); err == nil {
			return aiTime
		}
	}

	return time.Time{}
}

func (aa ApiAiOrder) Fulfiled() bool {
	return true
}

func (aa ApiAiOrder) Note() string {
	if imp, exist := aa.AiResult.Params["important"]; exist {
		return imp.(string)
	}

	return ""
}

// MODE 1 extracting, by key products
func (aa ApiAiOrder) ExtractProducts(t string) []Item {
	result := make([]Item, 0, 50)

	if products, exist := aa.AiResult.Params[t]; exist {
		ps := reflect.ValueOf(products)

		for i := 0; i < ps.Len(); i++ {
			p := ps.Index(i).Interface()

			switch v := p.(type) {
			case string:
				// name := p.(string)
				item := Item{Product: v}
				result = append(result, item)
			case map[string]interface{}:
				// itemMap := p.(map[string]interface{})
				name, _ := v["product"].(string)
				taste, _ := v["taste"].(string)

				if name != "" {
					item := Item{
						Product: name,
					}

					if taste != "" {
						item.Taste = taste
					}

					result = append(result, item)
				}
			}
		}
	}

	return result
}

// MODE 2 extracting, by key productItems
func (aa ApiAiOrder) ExtractProductItems(s string) []Item {
	result := make([]Item, 0)

	if prodItems, ok := aa.AiResult.Params[s]; ok {
		ps := reflect.ValueOf(prodItems)

		for i := 0; i < ps.Len(); i++ {
			var name, unit, spec, taste string
			var quantity int

			p := ps.Index(i)
			prodItem := p.Interface().(map[string]interface{})
			name, _ = prodItem["product"].(string)
			spec, _ = prodItem["spec"].(string)
			taste, _ = prodItem["taste"].(string)

			quanMap, _ := prodItem["quantity"].(map[string]interface{})
			numberFloat, ok := quanMap["number"].(float64)
			if ok {
				quantity = int(numberFloat)
			}

			unit, _ = quanMap["unit"].(string)

			item := Item{
				Product:  name,
				Quantity: quantity,
				Unit:     unit,
				Spec:     spec,
				Taste:    taste,
			}
			result = append(result, item)
		}
	}

	return result
}

func (aa ApiAiOrder) ExtractQuantities(t string) []Item {
	result := make([]Item, 0, 50)

	if quantities, exist := aa.AiResult.Params[t]; exist {
		qs := reflect.ValueOf(quantities)

		for i := 0; i < qs.Len(); i++ {
			q := qs.Index(i).Interface()

			switch v := q.(type) {
			case string:
				// qs := q.(string)
				qi := extractQuantity(v)
				item := Item{Quantity: qi}
				result = append(result, item)
			case float64:
				// qf := q.(float64)
				item := Item{Quantity: int(v)}
				result = append(result, item)
			case map[string]interface{}:
				// quanMap := q.(map[string]interface{})
				log.Printf("quantity: %v\n", v)
				qf, ok := v["number"].(float64)
				if !ok {
					qf, _ = v["quantity"].(float64)
				}

				quantity := int(qf)
				item := Item{Quantity: quantity}

				if unit, ok := v["unit"].(string); ok {
					unit = strings.Replace(unit, "龘", "", -1)
					unit = strings.Replace(unit, " ", "", -1)
					item.Unit = unit
				}

				result = append(result, item)
			default:
				log.Println("Unknown Quantity type: %v", t)
			}
		}
	}

	return result
}

func extractQuantity(s string) int {
	nums := strings.TrimFunc(s, TrimToNum)
	numsCgd := DBCtoSBC(nums)

	if len(numsCgd) > 0 {
		q, _ := strconv.Atoi(numsCgd)
		return q
	} else {
		return 0
	}
	// re := regexp.MustCompile("[0-9]+")
}

func TrimToNum(r rune) bool {
	if n := r - '0'; n >= 0 && n <= 9 {
		return false
	} else if m := r - '０'; m >= 0 && m <= 9 {
		return false
	}

	return true
}

func DBCtoSBC(s string) string {
	retstr := ""
	for _, i := range s {
		inside_code := i
		if inside_code == 12288 {
			inside_code = 32
		} else {
			inside_code -= 65248
		}
		if inside_code < 32 || inside_code > 126 {
			retstr += string(i)
		} else {
			retstr += string(inside_code)
		}
	}
	return retstr
}
<|endoftext|>


<commit_before>package resolves

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

	"github.com/hysios/apiai-go"
	"github.com/wanliu/brain_data/database"
	"github.com/wanliu/flow/builtin/ai"

	. "github.com/wanliu/flow/context"
)

// 处理开单的逻辑结构, 不需要是组件
// 作为context的一个部分，或者存在一个Value中
type OrderResolve struct {
	AiParams  ai.AiOrder
	Products  ItemsResolve
	Gifts     ItemsResolve
	Address   string
	Customer  string
	Time      time.Time
	DefTime   string
	Current   Resolve
	Note      string
	UpdatedAt time.Time
	Editing   bool
	Canceled  bool

	User *database.User
}

func NewOrderResolve(ctx Context) *OrderResolve {
	resolve := new(OrderResolve)
	resolve.Touch()

	aiResult := ctx.Value("Result").(apiai.Result)

	resolve.AiParams = ai.ApiAiOrder{AiResult: aiResult}
	resolve.ExtractFromParams()

	if viewer := ctx.Value("Viewer"); viewer != nil {
		user := viewer.(*database.User)
		resolve.User = user
	}

	return resolve
}

func (r *OrderResolve) Solve(aiResult apiai.Result) string {
	return r.Answer()
}

func (r *OrderResolve) Touch() {
	r.UpdatedAt = time.Now()
}

func (r OrderResolve) Modifable(expireMin int) bool {
	return !r.Expired(expireMin) || r.Submited()
}

// TODO
func (r OrderResolve) Cancelable() bool {
	return true
}

// TODO
func (r *OrderResolve) Cancel() bool {
	r.Canceled = true
	return true
}

func (r OrderResolve) Fulfiled() bool {
	return len(r.Products.Products) > 0 && (r.Address != "" || r.Customer != "")
}

func (r OrderResolve) Expired(expireMin int) bool {
	return r.UpdatedAt.Add(time.Duration(expireMin)*time.Minute).UnixNano() < time.Now().UnixNano()
}

// TODO
func (r OrderResolve) Submited() bool {
	return false
}

// 从ｌｕｉｓ数据构造结构数据
func (r *OrderResolve) ExtractFromParams() {
	r.ExtractItems()
	r.ExtractGiftItems()
	r.ExtractAddress()
	r.ExtractCustomer()
	r.ExtractTime()
	r.ExtractNote()
}

func (r *OrderResolve) ExtractItems() {
	for _, i := range r.AiParams.Items() {
		name := strings.Replace(i.Product, "%", "%%", -1)
		item := &ItemResolve{
			Resolved: true,
			Name:     name,
			Price:    i.Price,
			Quantity: i.Quantity,
			Product:  name,
		}

		r.Products.Products = append(r.Products.Products, item)
	}
}

func (r *OrderResolve) ExtractGiftItems() {
	for _, i := range r.AiParams.GiftItems() {
		name := strings.Replace(i.Product, "%", "%%", -1)
		item := &ItemResolve{
			Resolved: true,
			Name:     name,
			Price:    i.Price,
			Quantity: i.Quantity,
			Product:  name,
		}

		r.Gifts.Products = append(r.Gifts.Products, item)
	}
}

func (r *OrderResolve) ExtractAddress() {
	r.Address = r.AiParams.Address()
}

func (r *OrderResolve) ExtractCustomer() {
	r.Customer = r.AiParams.Customer()
}

func (r *OrderResolve) ExtractTime() {
	r.Time = r.AiParams.Time()
}

func (r *OrderResolve) ExtractNote() {
	r.Note = r.AiParams.Note()
}

func (r *OrderResolve) SetDefTime(t string) {
	r.DefTime = t

	if r.Time.IsZero() && r.DefTime != "" {
		r.SetTimeByDef()
	}
}

func (r *OrderResolve) SetTimeByDef() {
	if r.DefTime == "今天" {
		r.Time = time.Now()
	} else if r.DefTime == "明天" {
		r.Time = time.Now().Add(24 * time.Hour)
	}
}

func (r OrderResolve) EmptyProducts() bool {
	return len(r.Products.Products) == 0
}

func (r OrderResolve) Answer() string {
	if r.Fulfiled() {
		return r.PostOrderAndAnswer()
	} else {
		return r.AnswerHead() + r.AnswerFooter("", "")
	}
}

func (r *OrderResolve) PostOrderAndAnswer() string {
	items := make([]database.OrderItem, 0, 0)
	gifts := make([]database.GiftItem, 0, 0)

	for _, pr := range r.Products.Products {
		item, err := database.NewOrderItem("", pr.Product, uint(pr.Quantity), pr.Price)
		if err != nil {
			return err.Error()
		}
		items = append(items, *item)
	}

	for _, pr := range r.Gifts.Products {
		gift, err := database.NewGiftItem("", pr.Product, uint(pr.Quantity))
		if err != nil {
			return err.Error()
		}

		gifts = append(gifts, *gift)
	}

	if r.User == nil {
		// return "无法创建订单，请与工作人员联系！"
		user := database.User{}
		order, err := user.CreateSaledOrder(r.Address, r.Note, r.Time, 0, items, gifts)

		if err != nil {
			return err.Error()
		} else {
			return r.AnswerHead() + r.AnswerBody() + r.AnswerFooter(order.ID, order.GlobelId())
		}
	} else {
		order, err := r.User.CreateSaledOrder(r.Address, r.Note, r.Time, 0, items, gifts)

		if err != nil {
			return err.Error()
		} else {
			return r.AnswerHead() + r.AnswerBody() + r.AnswerFooter(order.ID, order.GlobelId())
		}
	}

}

func (r OrderResolve) AddressInfo() string {
	if r.Address != "" && r.Customer != "" {
		return "地址:" + r.Address + r.Customer + "\n"
	} else if r.Address != "" {
		return "地址:" + r.Address + "\n"
	} else if r.Customer != "" {
		return "客户:" + r.Customer + "\n"
	} else {
		return ""
	}
}

func (r OrderResolve) AnswerHead() string {
	desc := "订单正在处理, 已经添加" + CnNum(len(r.Products.Products)) + "种产品"

	if r.Fulfiled() {
		desc = "订单已经生成, 共" + CnNum(len(r.Products.Products)) + "种产品"
	}

	if len(r.Gifts.Products) > 0 {
		desc = desc + ", " + CnNum(len(r.Gifts.Products)) + "种赠品" + "\n"
	} else {
		desc = desc + "\n"
	}

	return desc
}

func (r OrderResolve) AnswerBody() string {
	desc := ""

	for _, p := range r.Products.Products {
		desc = desc + p.Product + " " + strconv.Itoa(p.Quantity) + "件\n"
	}

	if len(r.Gifts.Products) > 0 {
		desc = desc + "申请的赠品:\n"

		for _, g := range r.Gifts.Products {
			desc = desc + g.Product + " " + strconv.Itoa(g.Quantity) + "件\n"
		}
	}

	desc = desc + "时间:" + r.Time.Format("2006年01月02日") + "\n"

	if r.Note != "" {
		desc = desc + "备注：" + r.Note + "\n"
	}

	return desc
}

func (r OrderResolve) AnswerFooter(no, id interface{}) string {
	desc := ""

	if r.Fulfiled() {
		desc = desc + r.AddressInfo()
		desc = desc + "订单已经生成，订单号为：" + fmt.Sprint(no) + "\n"
		desc = desc + "订单入口: http://jiejie.wanliu.biz/order/QueryDetail/" + fmt.Sprint(id)
	} else {
		desc = desc + "还缺少收货地址或客户信息\n"
	}

	return desc
}

func CnNum(num int) string {
	switch num {
	case 1:
		return "一"
	case 2:
		return "两"
	case 3:
		return "三"
	case 4:
		return "四"
	case 5:
		return "五"
	case 6:
		return "六"
	case 7:
		return "七"
	case 8:
		return "八"
	case 9:
		return "九"
	case 10:
		return "十"
	default:
		return strconv.Itoa(num)
	}
}
<commit_msg>add storehouse id to order create<commit_after>package resolves

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

	"github.com/hysios/apiai-go"
	"github.com/wanliu/brain_data/database"
	"github.com/wanliu/flow/builtin/ai"

	. "github.com/wanliu/flow/context"
)

// 处理开单的逻辑结构, 不需要是组件
// 作为context的一个部分，或者存在一个Value中
type OrderResolve struct {
	AiParams  ai.AiOrder
	Products  ItemsResolve
	Gifts     ItemsResolve
	Address   string
	Customer  string
	Time      time.Time
	DefTime   string
	Current   Resolve
	Note      string
	UpdatedAt time.Time
	Editing   bool
	Canceled  bool

	User *database.User
}

func NewOrderResolve(ctx Context) *OrderResolve {
	resolve := new(OrderResolve)
	resolve.Touch()

	aiResult := ctx.Value("Result").(apiai.Result)

	resolve.AiParams = ai.ApiAiOrder{AiResult: aiResult}
	resolve.ExtractFromParams()

	if viewer := ctx.Value("Viewer"); viewer != nil {
		user := viewer.(*database.User)
		resolve.User = user
	}

	return resolve
}

func (r *OrderResolve) Solve(aiResult apiai.Result) string {
	return r.Answer()
}

func (r *OrderResolve) Touch() {
	r.UpdatedAt = time.Now()
}

func (r OrderResolve) Modifable(expireMin int) bool {
	return !r.Expired(expireMin) || r.Submited()
}

// TODO
func (r OrderResolve) Cancelable() bool {
	return true
}

// TODO
func (r *OrderResolve) Cancel() bool {
	r.Canceled = true
	return true
}

func (r OrderResolve) Fulfiled() bool {
	return len(r.Products.Products) > 0 && (r.Address != "" || r.Customer != "")
}

func (r OrderResolve) Expired(expireMin int) bool {
	return r.UpdatedAt.Add(time.Duration(expireMin)*time.Minute).UnixNano() < time.Now().UnixNano()
}

// TODO
func (r OrderResolve) Submited() bool {
	return false
}

// 从ｌｕｉｓ数据构造结构数据
func (r *OrderResolve) ExtractFromParams() {
	r.ExtractItems()
	r.ExtractGiftItems()
	r.ExtractAddress()
	r.ExtractCustomer()
	r.ExtractTime()
	r.ExtractNote()
}

func (r *OrderResolve) ExtractItems() {
	for _, i := range r.AiParams.Items() {
		name := strings.Replace(i.Product, "%", "%%", -1)
		item := &ItemResolve{
			Resolved: true,
			Name:     name,
			Price:    i.Price,
			Quantity: i.Quantity,
			Product:  name,
		}

		r.Products.Products = append(r.Products.Products, item)
	}
}

func (r *OrderResolve) ExtractGiftItems() {
	for _, i := range r.AiParams.GiftItems() {
		name := strings.Replace(i.Product, "%", "%%", -1)
		item := &ItemResolve{
			Resolved: true,
			Name:     name,
			Price:    i.Price,
			Quantity: i.Quantity,
			Product:  name,
		}

		r.Gifts.Products = append(r.Gifts.Products, item)
	}
}

func (r *OrderResolve) ExtractAddress() {
	r.Address = r.AiParams.Address()
}

func (r *OrderResolve) ExtractCustomer() {
	r.Customer = r.AiParams.Customer()
}

func (r *OrderResolve) ExtractTime() {
	r.Time = r.AiParams.Time()
}

func (r *OrderResolve) ExtractNote() {
	r.Note = r.AiParams.Note()
}

func (r *OrderResolve) SetDefTime(t string) {
	r.DefTime = t

	if r.Time.IsZero() && r.DefTime != "" {
		r.SetTimeByDef()
	}
}

func (r *OrderResolve) SetTimeByDef() {
	if r.DefTime == "今天" {
		r.Time = time.Now()
	} else if r.DefTime == "明天" {
		r.Time = time.Now().Add(24 * time.Hour)
	}
}

func (r OrderResolve) EmptyProducts() bool {
	return len(r.Products.Products) == 0
}

func (r OrderResolve) Answer() string {
	if r.Fulfiled() {
		return r.PostOrderAndAnswer()
	} else {
		return r.AnswerHead() + r.AnswerFooter("", "")
	}
}

func (r *OrderResolve) PostOrderAndAnswer() string {
	items := make([]database.OrderItem, 0, 0)
	gifts := make([]database.GiftItem, 0, 0)

	for _, pr := range r.Products.Products {
		item, err := database.NewOrderItem("", pr.Product, uint(pr.Quantity), pr.Price)
		if err != nil {
			return err.Error()
		}
		items = append(items, *item)
	}

	for _, pr := range r.Gifts.Products {
		gift, err := database.NewGiftItem("", pr.Product, uint(pr.Quantity))
		if err != nil {
			return err.Error()
		}

		gifts = append(gifts, *gift)
	}

	if r.User == nil {
		// return "无法创建订单，请与工作人员联系！"
		user := database.User{}
		order, err := user.CreateSaledOrder(r.Address, r.Note, r.Time, 0, 0, items, gifts)

		if err != nil {
			return err.Error()
		} else {
			return r.AnswerHead() + r.AnswerBody() + r.AnswerFooter(order.ID, order.GlobelId())
		}
	} else {
		order, err := r.User.CreateSaledOrder(r.Address, r.Note, r.Time, 0, 0, items, gifts)

		if err != nil {
			return err.Error()
		} else {
			return r.AnswerHead() + r.AnswerBody() + r.AnswerFooter(order.ID, order.GlobelId())
		}
	}

}

func (r OrderResolve) AddressInfo() string {
	if r.Address != "" && r.Customer != "" {
		return "地址:" + r.Address + r.Customer + "\n"
	} else if r.Address != "" {
		return "地址:" + r.Address + "\n"
	} else if r.Customer != "" {
		return "客户:" + r.Customer + "\n"
	} else {
		return ""
	}
}

func (r OrderResolve) AnswerHead() string {
	desc := "订单正在处理, 已经添加" + CnNum(len(r.Products.Products)) + "种产品"

	if r.Fulfiled() {
		desc = "订单已经生成, 共" + CnNum(len(r.Products.Products)) + "种产品"
	}

	if len(r.Gifts.Products) > 0 {
		desc = desc + ", " + CnNum(len(r.Gifts.Products)) + "种赠品" + "\n"
	} else {
		desc = desc + "\n"
	}

	return desc
}

func (r OrderResolve) AnswerBody() string {
	desc := ""

	for _, p := range r.Products.Products {
		desc = desc + p.Product + " " + strconv.Itoa(p.Quantity) + "件\n"
	}

	if len(r.Gifts.Products) > 0 {
		desc = desc + "申请的赠品:\n"

		for _, g := range r.Gifts.Products {
			desc = desc + g.Product + " " + strconv.Itoa(g.Quantity) + "件\n"
		}
	}

	desc = desc + "时间:" + r.Time.Format("2006年01月02日") + "\n"

	if r.Note != "" {
		desc = desc + "备注：" + r.Note + "\n"
	}

	return desc
}

func (r OrderResolve) AnswerFooter(no, id interface{}) string {
	desc := ""

	if r.Fulfiled() {
		desc = desc + r.AddressInfo()
		desc = desc + "订单已经生成，订单号为：" + fmt.Sprint(no) + "\n"
		desc = desc + "订单入口: http://jiejie.wanliu.biz/order/QueryDetail/" + fmt.Sprint(id)
	} else {
		desc = desc + "还缺少收货地址或客户信息\n"
	}

	return desc
}

func CnNum(num int) string {
	switch num {
	case 1:
		return "一"
	case 2:
		return "两"
	case 3:
		return "三"
	case 4:
		return "四"
	case 5:
		return "五"
	case 6:
		return "六"
	case 7:
		return "七"
	case 8:
		return "八"
	case 9:
		return "九"
	case 10:
		return "十"
	default:
		return strconv.Itoa(num)
	}
}
<|endoftext|>


<commit_before>package coretest

import (
	"io"
	"os"
	"path"
	"strings"
	"syscall"
	"testing"
	"time"
)

const (
	fleetctlBinPath = "/usr/bin/fleetctl"
	tryTimes = 5
	tryInterval = 500 * time.Millisecond
	serviceData = `[Unit]
Description=Hello World
[Service]
ExecStart=/bin/bash -c "while true; do echo \"Hello, world\"; sleep 1; done"
`
)

// TestFleetctlListMachines tests that 'fleetctl list-machines' works
// and print itself out at least.
func TestFleetctlListMachines(t *testing.T) {
	stdout, stderr, err := Run(fleetctlBinPath, "list-machines", "--no-legend")
	if err != nil {
		t.Fatalf("fleetctl list-machines failed with error: %v\nstdout: %s\nstderr: %s", err, stdout, stderr)
	}

	stdout = strings.TrimSpace(stdout)
	if len(strings.Split(stdout, "\n")) == 0 {
		t.Fatalf("Failed listing out at least one machine\nstdout: %s", stdout)
	}
}

func checkServiceState(name string, t *testing.T) (exist bool, active bool) {
	stdout, stderr, err := Run(fleetctlBinPath, "list-units", "--no-legend")
	if err != nil {
		t.Fatalf("fleetctl list-units failed with error: %v\nstdout: %s\nstderr: %s", err, stdout, stderr)
	}

	lines := strings.Split(stdout, "\n")
	for _, line := range lines {
		if !strings.Contains(line, name) {
			continue
		}
		exist = true
		if strings.Contains(line, "active") {
			active = true
			return
		}
		return
	}
	return
}

// TestFleetctlRunService tests that fleetctl could start, stop and destroy
// unit file.
func TestFleetctlRunService(t *testing.T) {
	serviceName := "hello.service"

	serviceFile, err := os.Create(path.Join(os.TempDir(), serviceName))
	if err != nil {
		t.Fatalf("Failed creating %v: %v", serviceName, err)
	}
	defer syscall.Unlink(serviceFile.Name())

	if _, err := io.WriteString(serviceFile, serviceData); err != nil {
		t.Fatalf("Failed writing %v: %v", serviceFile.Name(), err)
	}

	stdout, stderr, err := Run(fleetctlBinPath, "start", serviceFile.Name())
	if err != nil {
		t.Fatalf("fleetctl start failed with error: %v\nstdout: %s\nstderr: %s", err, stdout, stderr)
	}

	checkServiceActive := func() bool {
		exist, active := checkServiceState(serviceName, t)
		return exist && active
	}
	if !Retry(checkServiceActive, tryTimes, tryInterval) {
		t.Fatalf("Failed checking %v is active", serviceName)
	}

	stdout, stderr, err = Run(fleetctlBinPath, "stop", serviceName)
	if err != nil {
		t.Fatalf("fleetctl stop failed with error: %v\nstdout: %s\nstderr: %s", err, stdout, stderr)
	}

	checkServiceInactive := func() bool {
		exist, active := checkServiceState(serviceName, t)
		return exist && !active
	}
	if !Retry(checkServiceInactive, tryTimes, tryInterval) {
		t.Fatalf("Failed checking %v is inactive", serviceName)
	}

	stdout, stderr, err = Run(fleetctlBinPath, "destroy", serviceName)
	if err != nil {
		t.Fatalf("fleetctl destroy failed with error: %v\nstdout: %s\nstderr: %s", err, stdout, stderr)
	}

	checkServiceNonexist := func() bool {
		exist, _ := checkServiceState(serviceName, t)
		return !exist
	}
	if !Retry(checkServiceNonexist, tryTimes, tryInterval) {
		t.Fatalf("Failed checking %v is nonexist", serviceName)
	}
}
<commit_msg>fix(fleet): Use "running" to identify an active service<commit_after>package coretest

import (
	"io"
	"os"
	"path"
	"strings"
	"syscall"
	"testing"
	"time"
)

const (
	fleetctlBinPath = "/usr/bin/fleetctl"
	tryTimes = 5
	tryInterval = 500 * time.Millisecond
	serviceData = `[Unit]
Description=Hello World
[Service]
ExecStart=/bin/bash -c "while true; do echo \"Hello, world\"; sleep 1; done"
`
)

// TestFleetctlListMachines tests that 'fleetctl list-machines' works
// and print itself out at least.
func TestFleetctlListMachines(t *testing.T) {
	stdout, stderr, err := Run(fleetctlBinPath, "list-machines", "--no-legend")
	if err != nil {
		t.Fatalf("fleetctl list-machines failed with error: %v\nstdout: %s\nstderr: %s", err, stdout, stderr)
	}

	stdout = strings.TrimSpace(stdout)
	if len(strings.Split(stdout, "\n")) == 0 {
		t.Fatalf("Failed listing out at least one machine\nstdout: %s", stdout)
	}
}

func checkServiceState(name string, t *testing.T) (exist bool, active bool) {
	stdout, stderr, err := Run(fleetctlBinPath, "list-units", "--no-legend")
	if err != nil {
		t.Fatalf("fleetctl list-units failed with error: %v\nstdout: %s\nstderr: %s", err, stdout, stderr)
	}

	lines := strings.Split(stdout, "\n")
	for _, line := range lines {
		if !strings.Contains(line, name) {
			continue
		}
		exist = true
		if strings.Contains(line, "running") {
			active = true
			return
		}
		return
	}
	return
}

// TestFleetctlRunService tests that fleetctl could start, stop and destroy
// unit file.
func TestFleetctlRunService(t *testing.T) {
	serviceName := "hello.service"

	serviceFile, err := os.Create(path.Join(os.TempDir(), serviceName))
	if err != nil {
		t.Fatalf("Failed creating %v: %v", serviceName, err)
	}
	defer syscall.Unlink(serviceFile.Name())

	if _, err := io.WriteString(serviceFile, serviceData); err != nil {
		t.Fatalf("Failed writing %v: %v", serviceFile.Name(), err)
	}

	stdout, stderr, err := Run(fleetctlBinPath, "start", serviceFile.Name())
	if err != nil {
		t.Fatalf("fleetctl start failed with error: %v\nstdout: %s\nstderr: %s", err, stdout, stderr)
	}

	checkServiceActive := func() bool {
		exist, active := checkServiceState(serviceName, t)
		return exist && active
	}
	if !Retry(checkServiceActive, tryTimes, tryInterval) {
		t.Fatalf("Failed checking %v is active", serviceName)
	}

	stdout, stderr, err = Run(fleetctlBinPath, "stop", serviceName)
	if err != nil {
		t.Fatalf("fleetctl stop failed with error: %v\nstdout: %s\nstderr: %s", err, stdout, stderr)
	}

	checkServiceInactive := func() bool {
		exist, active := checkServiceState(serviceName, t)
		return exist && !active
	}
	if !Retry(checkServiceInactive, tryTimes, tryInterval) {
		t.Fatalf("Failed checking %v is inactive", serviceName)
	}

	stdout, stderr, err = Run(fleetctlBinPath, "destroy", serviceName)
	if err != nil {
		t.Fatalf("fleetctl destroy failed with error: %v\nstdout: %s\nstderr: %s", err, stdout, stderr)
	}

	checkServiceNonexist := func() bool {
		exist, _ := checkServiceState(serviceName, t)
		return !exist
	}
	if !Retry(checkServiceNonexist, tryTimes, tryInterval) {
		t.Fatalf("Failed checking %v is nonexist", serviceName)
	}
}
<|endoftext|>


<commit_before>//  Copyright (c) 2014 Couchbase, Inc.
//  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 main

import (
	"fmt"
	"sync"

	"github.com/couchbase/gomemcached"
	log "github.com/couchbaselabs/clog"

	"github.com/steveyen/cbdatasource"
)

type DCPMutation struct {
	delete    bool
	vbucketId uint16
	key       []byte
	seq       uint64
}

// Implements both Feed and cbdatasource.Receiver interfaces.
type DCPFeed struct {
	name       string
	url        string
	poolName   string
	bucketName string
	bucketUUID string
	pf         StreamPartitionFunc
	streams    map[string]Stream
	bds        cbdatasource.BucketDataSource

	m      sync.Mutex
	closed bool

	errs []error
	muts []*DCPMutation
	meta map[uint16][]byte

	numSnapshotStarts int
	numSetMetaDatas   int
	numGetMetaDatas   int
	numRollbacks      int
}

func NewDCPFeed(name, url, poolName, bucketName, bucketUUID string,
	pf StreamPartitionFunc, streams map[string]Stream) (*DCPFeed, error) {
	vbucketIds, err := ParsePartitionsToVBucketIds(streams)
	if err != nil {
		return nil, err
	}
	if len(vbucketIds) <= 0 {
		vbucketIds = nil
	}

	var authFunc cbdatasource.AuthFunc
	var options *cbdatasource.BucketDataSourceOptions

	feed := &DCPFeed{
		name:       name,
		url:        url,
		poolName:   poolName,
		bucketName: bucketName,
		bucketUUID: bucketUUID,
		pf:         pf,
		streams:    streams,
	}

	feed.bds, err = cbdatasource.NewBucketDataSource([]string{url},
		poolName, bucketName, bucketUUID,
		vbucketIds, authFunc, feed, options)
	if err != nil {
		return nil, err
	}

	return feed, nil
}

func (t *DCPFeed) Name() string {
	return t.name
}

func (t *DCPFeed) Start() error {
	log.Printf("DCPFeed.Start, name: %s", t.Name())
	return t.bds.Start()
}

func (t *DCPFeed) Close() error {
	t.m.Lock()
	if t.closed {
		t.m.Unlock()
		return fmt.Errorf("already closed")
	}
	t.closed = true
	t.m.Unlock()

	log.Printf("DCPFeed.Close, name: %s", t.Name())
	return t.bds.Close()
}

func (t *DCPFeed) Streams() map[string]Stream {
	return t.streams
}

// --------------------------------------------------------

func (r *DCPFeed) OnError(err error) {
	r.m.Lock()
	defer r.m.Unlock()

	log.Printf("DCPFeed.OnError: %s: %v\n", r.name, err)
	r.errs = append(r.errs, err)
}

func (r *DCPFeed) DataUpdate(vbucketId uint16, key []byte, seq uint64,
	req *gomemcached.MCRequest) error {
	log.Printf("DCPFeed.DataUpdate: %s: vbucketId: %d, key: %s, seq: %d, req: %v\n",
		r.name, vbucketId, key, seq, req)

	r.m.Lock()
	defer r.m.Unlock()

	r.muts = append(r.muts, &DCPMutation{
		delete:    false,
		vbucketId: vbucketId,
		key:       key,
		seq:       seq,
	})
	return nil
}

func (r *DCPFeed) DataDelete(vbucketId uint16, key []byte, seq uint64,
	req *gomemcached.MCRequest) error {
	log.Printf("DCPFeed.DataDelete: %s: vbucketId: %d, key: %s, seq: %d, req: %#v",
		r.name, vbucketId, key, seq, req)

	r.m.Lock()
	defer r.m.Unlock()

	r.muts = append(r.muts, &DCPMutation{
		delete:    true,
		vbucketId: vbucketId,
		key:       key,
		seq:       seq,
	})
	return nil
}

func (r *DCPFeed) SnapshotStart(vbucketId uint16,
	snapStart, snapEnd uint64, snapType uint32) error {
	log.Printf("DCPFeed.SnapshotStart: %s: vbucketId: %d,"+
		" snapStart: %d, snapEnd: %d, snapType: %d",
		r.name, vbucketId, snapStart, snapEnd, snapType)

	r.numSnapshotStarts += 1
	return nil
}

func (r *DCPFeed) SetMetaData(vbucketId uint16, value []byte) error {
	r.m.Lock()
	defer r.m.Unlock()

	r.numSetMetaDatas += 1
	if r.meta == nil {
		r.meta = make(map[uint16][]byte)
	}
	r.meta[vbucketId] = value
	return nil
}

func (r *DCPFeed) GetMetaData(vbucketId uint16) (value []byte, lastSeq uint64, err error) {
	r.m.Lock()
	defer r.m.Unlock()

	r.numGetMetaDatas += 1
	rv := []byte(nil)
	if r.meta != nil {
		rv = r.meta[vbucketId]
	}
	for i := len(r.muts) - 1; i >= 0; i = i - 1 {
		if r.muts[i].vbucketId == vbucketId {
			return rv, r.muts[i].seq, nil
		}
	}
	return rv, 0, nil
}

func (r *DCPFeed) Rollback(vbucketId uint16, rollbackSeq uint64) error {
	r.numRollbacks += 1
	return fmt.Errorf("bad-rollback")
}
<commit_msg>track in-memory high seq nums for DCPFeed<commit_after>//  Copyright (c) 2014 Couchbase, Inc.
//  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 main

import (
	"fmt"
	"sync"

	"github.com/couchbase/gomemcached"
	log "github.com/couchbaselabs/clog"

	"github.com/steveyen/cbdatasource"
)

// Implements both Feed and cbdatasource.Receiver interfaces.
type DCPFeed struct {
	name       string
	url        string
	poolName   string
	bucketName string
	bucketUUID string
	pf         StreamPartitionFunc
	streams    map[string]Stream
	bds        cbdatasource.BucketDataSource

	m       sync.Mutex
	closed  bool
	lastErr error

	seqs map[uint16]uint64 // To track max seq #'s we received per vbucketId.
	meta map[uint16][]byte // To track metadata blob's per vbucketId.

	numError         uint64
	numUpdate        uint64
	numDelete        uint64
	numSnapshotStart uint64
	numSetMetaData   uint64
	numGetMetaData   uint64
	numRollback      uint64
}

func NewDCPFeed(name, url, poolName, bucketName, bucketUUID string,
	pf StreamPartitionFunc, streams map[string]Stream) (*DCPFeed, error) {
	vbucketIds, err := ParsePartitionsToVBucketIds(streams)
	if err != nil {
		return nil, err
	}
	if len(vbucketIds) <= 0 {
		vbucketIds = nil
	}

	var authFunc cbdatasource.AuthFunc
	var options *cbdatasource.BucketDataSourceOptions

	feed := &DCPFeed{
		name:       name,
		url:        url,
		poolName:   poolName,
		bucketName: bucketName,
		bucketUUID: bucketUUID,
		pf:         pf,
		streams:    streams,
	}

	feed.bds, err = cbdatasource.NewBucketDataSource([]string{url},
		poolName, bucketName, bucketUUID,
		vbucketIds, authFunc, feed, options)
	if err != nil {
		return nil, err
	}

	return feed, nil
}

func (t *DCPFeed) Name() string {
	return t.name
}

func (t *DCPFeed) Start() error {
	log.Printf("DCPFeed.Start, name: %s", t.Name())
	return t.bds.Start()
}

func (t *DCPFeed) Close() error {
	t.m.Lock()
	if t.closed {
		t.m.Unlock()
		return fmt.Errorf("already closed")
	}
	t.closed = true
	t.m.Unlock()

	log.Printf("DCPFeed.Close, name: %s", t.Name())
	return t.bds.Close()
}

func (t *DCPFeed) Streams() map[string]Stream {
	return t.streams
}

// --------------------------------------------------------

func (r *DCPFeed) OnError(err error) {
	log.Printf("DCPFeed.OnError: %s: %v\n", r.name, err)

	r.m.Lock()
	defer r.m.Unlock()
	r.numError += 1

	r.lastErr = err
}

func (r *DCPFeed) DataUpdate(vbucketId uint16, key []byte, seq uint64,
	req *gomemcached.MCRequest) error {
	// log.Printf("DCPFeed.DataUpdate: %s: vbucketId: %d, key: %s, seq: %d, req: %v\n",
	// r.name, vbucketId, key, seq, req)

	r.m.Lock()
	defer r.m.Unlock()
	r.numUpdate += 1

	r.updateSeqUnlocked(vbucketId, seq)
	return nil
}

func (r *DCPFeed) DataDelete(vbucketId uint16, key []byte, seq uint64,
	req *gomemcached.MCRequest) error {
	// log.Printf("DCPFeed.DataDelete: %s: vbucketId: %d, key: %s, seq: %d, req: %#v",
	// r.name, vbucketId, key, seq, req)

	r.m.Lock()
	defer r.m.Unlock()
	r.numDelete += 1

	r.updateSeqUnlocked(vbucketId, seq)
	return nil
}

func (r *DCPFeed) updateSeqUnlocked(vbucketId uint16, seq uint64) {
	if r.seqs == nil {
		r.seqs = make(map[uint16]uint64)
	}
	if r.seqs[vbucketId] < seq {
		r.seqs[vbucketId] = seq // Remember the max seq for GetMetaData().
	}
}

func (r *DCPFeed) SnapshotStart(vbucketId uint16,
	snapStart, snapEnd uint64, snapType uint32) error {
	log.Printf("DCPFeed.SnapshotStart: %s: vbucketId: %d,"+
		" snapStart: %d, snapEnd: %d, snapType: %d",
		r.name, vbucketId, snapStart, snapEnd, snapType)

	r.m.Lock()
	defer r.m.Unlock()
	r.numSnapshotStart += 1

	return nil
}

func (r *DCPFeed) SetMetaData(vbucketId uint16, value []byte) error {
	log.Printf("DCPFeed.SetMetaData: %s: vbucketId: %d,"+
		" value: %s", r.name, vbucketId, value)

	r.m.Lock()
	defer r.m.Unlock()
	r.numSetMetaData += 1

	if r.meta == nil {
		r.meta = make(map[uint16][]byte)
	}
	r.meta[vbucketId] = value

	return nil
}

func (r *DCPFeed) GetMetaData(vbucketId uint16) (value []byte, lastSeq uint64, err error) {
	log.Printf("DCPFeed.GetMetaData: %s: vbucketId: %d", r.name, vbucketId)

	r.m.Lock()
	defer r.m.Unlock()
	r.numGetMetaData += 1

	rv := []byte(nil)
	if r.meta != nil {
		rv = r.meta[vbucketId]
	}

	if r.seqs != nil {
		lastSeq = r.seqs[vbucketId]
	}

	return rv, lastSeq, nil
}

func (r *DCPFeed) Rollback(vbucketId uint16, rollbackSeq uint64) error {
	log.Printf("DCPFeed.Rollback: %s: vbucketId: %d,"+
		" rollbackSeq: %d", r.name, vbucketId, rollbackSeq)

	r.m.Lock()
	defer r.m.Unlock()
	r.numRollback += 1

	return fmt.Errorf("bad-rollback")
}
<|endoftext|>


<commit_before>/*
Package plotter abstracts the details of other chart packages into a simple
interface composed of Figures, Charts, and Data
*/
package plotter

import (
	"image"
	"image/color"
	"image/png"
	"io"
	"os"
	"path"
	"time"

	// third-party
	"code.google.com/p/freetype-go/freetype"
	"code.google.com/p/freetype-go/freetype/truetype"
	//"github.com/vdobler/chart"
	//"github.com/vdobler/chart/imgg"
	"github.com/stephen-soltesz/chart"
	"github.com/stephen-soltesz/chart/imgg"
)

type Style chart.Style

type Figure struct {
	Charts   []*Chart
	fontName string
}

var fontCache map[string]*truetype.Font

type Chart struct {
	chart.ScatterChart
}

type Data struct {
	chart.ScatterChartData
}

func autoStyle(s int) func() Style {
	i := s
	l := len(chart.StandardColors)
	return func() Style {
		i = (i + 1) % l
		return Style{Symbol: '.',
			SymbolSize:  1,
			SymbolColor: chart.StandardColors[i],
			LineColor:   chart.StandardColors[i],
			LineStyle:   chart.SolidLine,
			LineWidth:   2,
			FillColor:   chart.StandardColors[i]}
	}
}

var NextStyle = autoStyle(0)

func loadFont(fontfile string) *truetype.Font {
	var font *truetype.Font
	var ok bool
	var err error
	var data []byte

	// Read the font data once.
	if fontCache == nil {
		fontCache = make(map[string]*truetype.Font)
	}
	fontpath := "res/" + fontfile
	if font, ok = fontCache[fontpath]; !ok {
		data, err = Asset(fontpath)
		if err != nil {
			return nil
		}
		font, err = freetype.ParseFont(data)
		if err != nil {
			return nil
		}
		fontCache[fontpath] = font
	}
	return font
}

func NewFigure() *Figure {
	f := Figure{}
	f.fontName = "FreeSans.ttf"
	return &f
}

func (f *Figure) RenderFile(filename string, width, height int) error {
	var err error
	var imgFile *os.File
	dir, file := path.Split(filename)
	tmpname := dir + "/.tmp_" + file

	imgFile, err = os.Create(tmpname)
	if err != nil {
		return err
	}
	err = f.Render(imgFile, width, height)
	if err != nil {
		return err
	}
	err = imgFile.Close()
	if err != nil {
		return err
	}
	err = os.Rename(tmpname, filename)
	if err != nil {
		return err
	}
	return nil
}

func (f *Figure) Render(writer io.Writer, width, height int) error {
	whiteBG := color.RGBA{0xee, 0xee, 0xee, 0xff}
	image := image.NewRGBA(image.Rect(0, 0, width, height))
	count := len(f.Charts)
	font := loadFont(f.fontName)

	for i, ax := range f.Charts {
		igr := imgg.AddTo(image, 0, i*(height/count), width, (height / count), whiteBG, font, nil)
		ax.Plot(igr)
	}

	return png.Encode(writer, image)
}

func (f *Figure) AddChart(title, xlabel, ylabel string, xmin, xmax float64, usetime bool) *Chart {
	axis := Chart{chart.ScatterChart{}}

	axis.Title = title
	axis.XRange.Label = xlabel
	axis.YRange.Label = ylabel
	axis.XRange.ShowZero = false
	axis.XRange.TicSetting.Mirror = chart.MirrorAxisOnly
	axis.YRange.TicSetting.Mirror = chart.MirrorAxisOnly
	axis.XRange.TicSetting.Grid = chart.GridLines
	axis.YRange.TicSetting.Grid = chart.GridLines
	axis.XRange.Time = usetime
	if usetime {
		axis.XRange.TFixed(time.Unix(int64(xmin), 0), time.Unix(int64(xmax), 0), nil)
		// TODO make configurable
		//axis.YRange.Log = true
	} else {
		axis.XRange.Fixed(xmin, xmax, 0)
	}
	axis.XRange.Init()

	fSmall := chart.Font{Size: chart.NormalFontSize}
	//sKey := chart.Style{LineColor: color.NRGBA{0x0f, 0x0f, 0x0f, 0xff},
	//                    LineStyle: chart.SolidLine, LineWidth: 1,
	//                    FillColor: color.NRGBA{0xf8, 0xf8, 0xf8, 0xff},
	//                    Font: chart.Font{Size: chart.NormalFontSize}}

	sGrid := chart.Style{LineStyle: chart.DottedLine, LineColor: color.Gray{0xcc}}
	sZero := chart.Style{LineStyle: chart.DottedLine, LineColor: color.Gray{0xcc}}
	sMajor := chart.Style{LineColor: color.Gray{0x88}, Font: fSmall}
	sTic := chart.Style{LineColor: color.Gray{0x88}, Font: fSmall}
	sRange := chart.Style{Font: fSmall}
	//sBg := chart.Style{LineColor: color.NRGBA{0xff, 0xff, 0xee, 0x88},
	//                   FillColor: color.NRGBA{0xff, 0xff, 0xee, 0x88}}
	sTitle := chart.Style{Font: chart.Font{"Arial", chart.NormalFontSize, color.Gray{0x88}}}

	axis.Options = chart.PlotOptions{chart.GridLineElement: sGrid,
		//                                  chart.PlotBackgroundElement: sBg,
		chart.ZeroAxisElement:  sZero,
		chart.MajorAxisElement: sMajor,
		chart.MinorAxisElement: sMajor,
		chart.MajorTicElement:  sTic,
		chart.MinorTicElement:  sTic,
		//chart.KeyElement: sKey,
		chart.RangeLimitElement: sRange,
		chart.TitleElement:      sTitle}
	axis.Key.Cols = 1
	axis.Key.Pos = "itl"

	f.Charts = append(f.Charts, &axis)
	return &axis
}

func (ax *Chart) ShowXAxis(s bool) {
	ax.XRange.TicSetting.HideLabels = !s
	return
}

func (ax *Chart) AddData(name string, x, y []float64, style Style) *Data {
	ax.AddDataPair(name, x, y, chart.PlotStyleLinesPoints, chart.Style(style))
	line := Data{ax.Data[len(ax.Data)-1]}
	return &line
}

//func (ax *Chart) LenDatas() int {
//  return len(ax.Data)
//}

/*
// must be called after a plot
func (ax *Chart) PlotArea() (int, int, int, int) {
  x1 := ax.XRange.Data2Screen(ax.XRange.Min)
  y1 := ax.YRange.Data2Screen(ax.YRange.Min)
  w1 := ax.XRange.Data2Screen(ax.XRange.Max) - ax.XRange.Data2Screen(ax.XRange.Min)
  h1 := ax.YRange.Data2Screen(ax.YRange.Max) - ax.YRange.Data2Screen(ax.YRange.Min)
  return x1, y1, w1, h1
}
*/

//func (l *Data) GetName() string {
//  return l.Name
//}
<commit_msg>add svg rendering support.<commit_after>/*
Package plotter abstracts the details of other chart packages into a simple
interface composed of Figures, Charts, and Data
*/
package plotter

import (
	"image"
	"image/color"
	"image/png"
	"io"
	"os"
	"path"
	"time"

	// third-party
	"code.google.com/p/freetype-go/freetype"
	"code.google.com/p/freetype-go/freetype/truetype"
	"github.com/vdobler/chart"
	"github.com/vdobler/chart/imgg"
	"github.com/vdobler/chart/svgg"
	//"github.com/stephen-soltesz/chart"
	//"github.com/stephen-soltesz/chart/imgg"

	"github.com/ajstarks/svgo"
	//"github.com/stephen-soltesz/chart/svgg"
)

type Style chart.Style

type Figure struct {
	Charts   []*Chart
	fontName string
}

var fontCache map[string]*truetype.Font

type Chart struct {
	chart.ScatterChart
}

type Data struct {
	chart.ScatterChartData
}

func autoStyle(s int) func() Style {
	i := s
	l := len(chart.StandardColors)
	return func() Style {
		i = (i + 1) % l
		return Style{Symbol: '.',
			SymbolSize:  1,
			SymbolColor: chart.StandardColors[i],
			LineColor:   chart.StandardColors[i],
			LineStyle:   chart.SolidLine,
			LineWidth:   2,
			FillColor:   chart.StandardColors[i]}
	}
}

var NextStyle = autoStyle(0)

func loadFont(fontfile string) *truetype.Font {
	var font *truetype.Font
	var ok bool
	var err error
	var data []byte

	// Read the font data once.
	if fontCache == nil {
		fontCache = make(map[string]*truetype.Font)
	}
	fontpath := "res/" + fontfile
	if font, ok = fontCache[fontpath]; !ok {
		data, err = Asset(fontpath)
		if err != nil {
			return nil
		}
		font, err = freetype.ParseFont(data)
		if err != nil {
			return nil
		}
		fontCache[fontpath] = font
	}
	return font
}

func NewFigure() *Figure {
	f := Figure{}
	f.fontName = "FreeSans.ttf"
	return &f
}

func (f *Figure) RenderFile(filename string, width, height int) error {
	var err error
	var imgFile *os.File
	dir, file := path.Split(filename)
	tmpname := dir + "/.tmp_" + file

	imgFile, err = os.Create(tmpname)
	if err != nil {
		return err
	}
	err = f.Render(imgFile, width, height)
	if err != nil {
		return err
	}
	err = imgFile.Close()
	if err != nil {
		return err
	}
	err = os.Rename(tmpname, filename)
	if err != nil {
		return err
	}
	return nil
}

func (f *Figure) RenderSVG(writer io.Writer, width, height int) error {
	whiteBG := color.RGBA{0xee, 0xee, 0xee, 0xff}
	count := len(f.Charts)

	svgdata := svg.New(writer)
	svgdata.Start(width+10, height+10)
	svgdata.Rect(0, 0, width, height, "fill: #ffffff")

	for i, ax := range f.Charts {
		igr := svgg.AddTo(svgdata, 0, i*(height/count), width, (height / count), "", 12, whiteBG)
		ax.Plot(igr)
	}

	svgdata.End()
	return nil
}

func (f *Figure) Render(writer io.Writer, width, height int) error {
	whiteBG := color.RGBA{0xee, 0xee, 0xee, 0xff}
	image := image.NewRGBA(image.Rect(0, 0, width, height))
	count := len(f.Charts)
	font := loadFont(f.fontName)

	for i, ax := range f.Charts {
		igr := imgg.AddTo(image, 0, i*(height/count), width, (height / count), whiteBG, font, nil)
		ax.Plot(igr)
	}

	return png.Encode(writer, image)
}

func (f *Figure) AddChart(title, xlabel, ylabel string, xmin, xmax float64, usetime bool) *Chart {
	axis := Chart{chart.ScatterChart{}}

	axis.Title = title
	axis.XRange.Label = xlabel
	axis.YRange.Label = ylabel
	axis.XRange.ShowZero = false
	axis.XRange.TicSetting.Mirror = chart.MirrorAxisOnly
	axis.YRange.TicSetting.Mirror = chart.MirrorAxisOnly
	axis.XRange.TicSetting.Grid = chart.GridLines
	axis.YRange.TicSetting.Grid = chart.GridLines
	axis.XRange.Time = usetime
	if usetime {
		axis.XRange.TFixed(time.Unix(int64(xmin), 0), time.Unix(int64(xmax), 0), nil)
		// TODO make configurable
		//axis.YRange.Log = true
	} else {
		axis.XRange.Fixed(xmin, xmax, 0)
	}
	axis.XRange.Init()

	fSmall := chart.Font{Size: chart.NormalFontSize}
	//sKey := chart.Style{LineColor: color.NRGBA{0x0f, 0x0f, 0x0f, 0xff},
	//                    LineStyle: chart.SolidLine, LineWidth: 1,
	//                    FillColor: color.NRGBA{0xf8, 0xf8, 0xf8, 0xff},
	//                    Font: chart.Font{Size: chart.NormalFontSize}}

	sGrid := chart.Style{LineStyle: chart.DashedLine, LineColor: color.Gray{0xbb}, LineWidth: 1}
	sZero := chart.Style{LineStyle: chart.DashedLine, LineColor: color.Gray{0xbb}}
	sMajor := chart.Style{LineColor: color.Gray{0x88}, Font: fSmall, LineWidth: 1}
	sTic := chart.Style{LineColor: color.Gray{0x88}, Font: fSmall, LineWidth: 1}
	sRange := chart.Style{Font: fSmall, LineWidth: 1}
	//sBg := chart.Style{LineColor: color.NRGBA{0xff, 0xff, 0xee, 0x88},
	//                   FillColor: color.NRGBA{0xff, 0xff, 0xee, 0x88}}
	sTitle := chart.Style{Font: chart.Font{"Arial", chart.NormalFontSize, color.Gray{0x88}}, LineWidth: 1}

	axis.Options = chart.PlotOptions{chart.GridLineElement: sGrid,
		//                                  chart.PlotBackgroundElement: sBg,
		chart.ZeroAxisElement:  sZero,
		chart.MajorAxisElement: sMajor,
		chart.MinorAxisElement: sMajor,
		chart.MajorTicElement:  sTic,
		chart.MinorTicElement:  sTic,
		//chart.KeyElement: sKey,
		chart.RangeLimitElement: sRange,
		chart.TitleElement:      sTitle}
	axis.Key.Cols = 1
	axis.Key.Pos = "itl"

	f.Charts = append(f.Charts, &axis)
	return &axis
}

func (ax *Chart) ShowXAxis(s bool) {
	ax.XRange.TicSetting.HideLabels = !s
	return
}

func (ax *Chart) AddData(name string, x, y []float64, style Style) *Data {
	ax.AddDataPair(name, x, y, chart.PlotStyleLinesPoints, chart.Style(style))
	line := Data{ax.Data[len(ax.Data)-1]}
	return &line
}

//func (ax *Chart) LenDatas() int {
//  return len(ax.Data)
//}

/*
// must be called after a plot
func (ax *Chart) PlotArea() (int, int, int, int) {
  x1 := ax.XRange.Data2Screen(ax.XRange.Min)
  y1 := ax.YRange.Data2Screen(ax.YRange.Min)
  w1 := ax.XRange.Data2Screen(ax.XRange.Max) - ax.XRange.Data2Screen(ax.XRange.Min)
  h1 := ax.YRange.Data2Screen(ax.YRange.Max) - ax.YRange.Data2Screen(ax.YRange.Min)
  return x1, y1, w1, h1
}
*/

//func (l *Data) GetName() string {
//  return l.Name
//}
<|endoftext|>


<commit_before>// dacloud.go

package dacloud

/*

#include <dacloud.h>
#include <stdlib.h>

typedef void * dago;
struct dago_cloud {
	struct da_cloud_config dcc;
	char cfile[PATH_MAX];
	int r;
};

typedef struct dago_cloud dago_cloud_t;

dago*
dago_cloud_init(char *cfile)
{
	dago_cloud_t *d = malloc(sizeof(*d));
	strncpy(d->cfile, cfile, sizeof(d->cfile) - 1);
	d->cfile[sizeof(d->cfile) - 1] = '\0';
	return ((dago)d);
}

int
dago_cloud_load(dago _d)
{
	if (_d) {
		dago_cloud_t *d = (dago_cloud_t *)_d;
		return (da_cloud_init(&d->dcc, d->cfile));
	}

	return (-1);
}

void
dago_cloud_free(dago _d)
{
	if (_d) {
		dago_cloud_t *d = (dago_cloud_t *)_d;
		da_cloud_fini(&d->dcc);
		free(d);
	}
}

struct dago_detect {
	struct da_cloud_config *dcc;
	struct da_cloud_header_head h;
	struct da_cloud_property_head p;
	struct da_cloud_property *pp;
	int hr;
};

typedef struct dago_detect dago_detect_t;

dago
dago_cloud_header_init(dago _d)
{
	if (_d) {
		dago_cloud_t *d = (dago_cloud_t *)_d;
		dago_detect_t *dd = malloc(sizeof(*dd));
		dd->dcc = &d->dcc;
		dd->hr = da_cloud_header_init(&dd->h);
		return (dd);
	}

	return (NULL);
}

int
dago_cloud_detect(dago _d)
{
	if (_d) {
		dago_detect_t *d = (dago_detect_t *)_d;
		return (da_cloud_detect(d->dcc, &d->h, &d->p));
	}

	return (-1);
}

void
dago_detect_fini(dago _d)
{
	if (_d) {
		dago_detect_t *d = (dago_detect_t *)_d;
		da_cloud_properties_free(&d->p);
		da_cloud_header_free(&d->h);
	}
}

dago
dago_prop_next(dago _d)
{
	struct da_cloud_property *d = (struct da_cloud_property *)_d;
	return (SLIST_NEXT(d, entries));
}

typedef enum da_cloud_property_type da_cloud_property_type;

da_cloud_property_type
dago_prop_type(dago _d)
{
	struct da_cloud_property *d = (struct da_cloud_property *)_d;
	return (d->type);
}

long
dago_prop_getinteger(dago _d)
{
	struct da_cloud_property *d = (struct da_cloud_property *)_d;
	return (d->value.l);
}

char *
dago_prop_getstring(dago _d)
{
	struct da_cloud_property *d = (struct da_cloud_property *)_d;
	return (d->value.s);
}

typedef struct da_cloud_property da_cloud_property_t;
#cgo LDFLAGS: -L. -L/usr/local/lib -ldacloud
#cgo CFLAGS: -I. -I/usr/local/include
*/
import "C"
import "unsafe"

type DaGo struct {
	dc C.dago;
	R int;
}

func Init(cfile string) DaGo {
	var ret DaGo;
	_cfile := C.CString(cfile);
	ret.dc = (C.dago)(C.dago_cloud_init(_cfile));
	ret.R = (int)(C.dago_cloud_load(ret.dc));
	defer C.free((unsafe.Pointer)(_cfile));
	return ret;
}

func Detect(f DaGo, hdrs map[string]string) map[string]interface{} {
	ret := make(map[string]interface{});
	det := (*C.dago_detect_t)(C.dago_cloud_header_init(f.dc));

	if (det.hr == 0) {
		for k, v := range hdrs {
			key := C.CString(k);
			val := C.CString(v);

			C.da_cloud_header_add(&det.h, key, val);

			defer C.free((unsafe.Pointer)(val));
			defer C.free((unsafe.Pointer)(key));
		}

		if (C.dago_cloud_detect(det) == 0) {
			for det.pp = det.p.list.slh_first;
			    det.pp != nil;
			    det.pp = (*C.da_cloud_property_t)(C.dago_prop_next(det.pp)) {
				    ptype := (C.da_cloud_property_type)(C.dago_prop_type(det.pp));
				    pkey := C.GoString(det.pp.name);

				    switch (ptype) {
				    case C.DA_CLOUD_LONG:
					    value := C.dago_prop_getinteger(det.pp);
					    ret[pkey] = (int)(value);
					    break;
				    case C.DA_CLOUD_BOOL:
					    value := C.dago_prop_getinteger(det.pp);
					    if value == 1 {
						    ret[pkey] = true;
					    } else {
						    ret[pkey] = false;
					    }
					    break;
				    case C.DA_CLOUD_STRING, C.DA_CLOUD_UNKNOWN:
					    value := C.dago_prop_getstring(det.pp);
					    ret[pkey] = C.GoString(value);
					    break;
				}
			}
		}

		C.dago_detect_fini(det);
	}

	return ret;
}

func Finalize(f DaGo) {
	C.dago_cloud_free(f.dc);
}
<commit_msg>avoiding allocation on the heap<commit_after>// dacloud.go

package dacloud

/*

#include <dacloud.h>
#include <stdlib.h>

typedef void * dago;
struct dago_cloud {
	struct da_cloud_config dcc;
	char cfile[PATH_MAX];
	int r;
};

typedef struct dago_cloud dago_cloud_t;

dago*
dago_cloud_init(char *cfile)
{
	dago_cloud_t *d = malloc(sizeof(*d));
	strncpy(d->cfile, cfile, sizeof(d->cfile) - 1);
	d->cfile[sizeof(d->cfile) - 1] = '\0';
	return ((dago)d);
}

int
dago_cloud_load(dago _d)
{
	if (_d) {
		dago_cloud_t *d = (dago_cloud_t *)_d;
		return (da_cloud_init(&d->dcc, d->cfile));
	}

	return (-1);
}

void
dago_cloud_free(dago _d)
{
	if (_d) {
		dago_cloud_t *d = (dago_cloud_t *)_d;
		da_cloud_fini(&d->dcc);
		free(d);
	}
}

struct dago_detect {
	struct da_cloud_config *dcc;
	struct da_cloud_header_head h;
	struct da_cloud_property_head p;
	struct da_cloud_property *pp;
	int hr;
};

typedef struct dago_detect dago_detect_t;

dago
dago_cloud_header_init(dago _d)
{
	if (_d) {
		dago_cloud_t *d = (dago_cloud_t *)_d;
		dago_detect_t *dd = malloc(sizeof(*dd));
		dd->dcc = &d->dcc;
		dd->hr = da_cloud_header_init(&dd->h);
		return (dd);
	}

	return (NULL);
}

int
dago_cloud_detect(dago _d)
{
	if (_d) {
		dago_detect_t *d = (dago_detect_t *)_d;
		return (da_cloud_detect(d->dcc, &d->h, &d->p));
	}

	return (-1);
}

void
dago_detect_fini(dago _d)
{
	if (_d) {
		dago_detect_t *d = (dago_detect_t *)_d;
		da_cloud_properties_free(&d->p);
		da_cloud_header_free(&d->h);
	}
}

dago
dago_prop_next(dago _d)
{
	struct da_cloud_property *d = (struct da_cloud_property *)_d;
	return (SLIST_NEXT(d, entries));
}

typedef enum da_cloud_property_type da_cloud_property_type;

da_cloud_property_type
dago_prop_type(dago _d)
{
	struct da_cloud_property *d = (struct da_cloud_property *)_d;
	return (d->type);
}

long
dago_prop_getinteger(dago _d)
{
	struct da_cloud_property *d = (struct da_cloud_property *)_d;
	return (d->value.l);
}

char *
dago_prop_getstring(dago _d)
{
	struct da_cloud_property *d = (struct da_cloud_property *)_d;
	return (d->value.s);
}

typedef struct da_cloud_property da_cloud_property_t;
#cgo LDFLAGS: -L. -L/usr/local/lib -ldacloud
#cgo CFLAGS: -I. -I/usr/local/include
*/
import "C"
import "math"
import "unsafe"

type DaGo struct {
	dc C.dago;
	R int;
}

func Init(cfile string) DaGo {
	var ret DaGo;
	_cfile := C.CString(cfile);
	ret.dc = (C.dago)(C.dago_cloud_init(_cfile));
	ret.R = (int)(C.dago_cloud_load(ret.dc));
	defer C.free((unsafe.Pointer)(_cfile));
	return ret;
}

func Detect(f DaGo, hdrs map[string]string) map[string]interface{} {
	ret := make(map[string]interface{});
	det := (*C.dago_detect_t)(C.dago_cloud_header_init(f.dc));

	if (det.hr == 0) {
		for k, v := range hdrs {
			var key [64]C.char;
			var val [256]C.char;
			bkey := []byte(k);
			bval := []byte(v);
			ksz := (C.size_t)(math.Min(float64(len(bkey)), float64(unsafe.Sizeof(key))));
			vsz := (C.size_t)(math.Min(float64(len(bkey)), float64(unsafe.Sizeof(key))));
			C.memcpy(unsafe.Pointer(&key[0]), unsafe.Pointer(&bkey[0]), ksz);
			C.memcpy(unsafe.Pointer(&val[0]), unsafe.Pointer(&bval[0]), vsz);
			key[ksz] = 0;
			val[vsz] = 0;

			C.da_cloud_header_add(&det.h, &key[0], &val[0]);
		}

		if (C.dago_cloud_detect(det) == 0) {
			for det.pp = det.p.list.slh_first;
			    det.pp != nil;
			    det.pp = (*C.da_cloud_property_t)(C.dago_prop_next(det.pp)) {
				    ptype := (C.da_cloud_property_type)(C.dago_prop_type(det.pp));
				    pkey := C.GoString(det.pp.name);

				    switch (ptype) {
				    case C.DA_CLOUD_LONG:
					    value := C.dago_prop_getinteger(det.pp);
					    ret[pkey] = (int)(value);
					    break;
				    case C.DA_CLOUD_BOOL:
					    value := C.dago_prop_getinteger(det.pp);
					    if value == 1 {
						    ret[pkey] = true;
					    } else {
						    ret[pkey] = false;
					    }
					    break;
				    case C.DA_CLOUD_STRING, C.DA_CLOUD_UNKNOWN:
					    value := C.dago_prop_getstring(det.pp);
					    ret[pkey] = C.GoString(value);
					    break;
				}
			}
		}

		C.dago_detect_fini(det);
	}

	return ret;
}

func Finalize(f DaGo) {
	C.dago_cloud_free(f.dc);
}
<|endoftext|>


<commit_before>// Copyright 2014 Matt Martz <matt@sivel.net>
// All Rights Reserved.
//
//    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 main

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"os/user"
	"path"
	"path/filepath"
	"regexp"
	"strings"
	"syscall"

	"github.com/wm/powerline-shell-go/powerline"
)

func getCurrentWorkingDir() (string, []string) {
	dir, err := filepath.Abs(".")
	if err != nil {
		log.Fatal(err)
	}
	userDir := strings.Replace(dir, os.Getenv("HOME"), "~", 1)
	userDir = strings.TrimSuffix(userDir, "/")
	parts := strings.Split(userDir, "/")
	return dir, parts
}

func getVirtualEnv() string {
	virtualEnv := os.Getenv("VIRTUAL_ENV")
	if virtualEnv == "" {
		return ""
	}

	virtualEnvName := path.Base(virtualEnv)
	return virtualEnvName
}

func isWritableDir(dir string) bool {
	tmpPath := path.Join(dir, ".powerline-write-test")
	_, err := os.Create(tmpPath)
	if err != nil {
		return false
	}
	os.Remove(tmpPath)
	return true
}

func getGitInformation() (string, bool) {
	var status string
	var staged bool
	stdout, _ := exec.Command("git", "status", "--ignore-submodules").Output()
	reBranch := regexp.MustCompile(`^(HEAD detached at|HEAD detached from|# On branch|On branch) (\S+)`)
	matchBranch := reBranch.FindStringSubmatch(string(stdout))
	if len(matchBranch) > 0 {
		if matchBranch[2] == "detached" {
			status = matchBranch[2]
		} else {
			status = matchBranch[2]
		}
	}

	reStatus := regexp.MustCompile(`Your branch is (ahead|behind).*?([0-9]+) comm`)
	matchStatus := reStatus.FindStringSubmatch(string(stdout))
	if len(matchStatus) > 0 {
		status = fmt.Sprintf("%s %s", status, matchStatus[2])
		if matchStatus[1] == "behind" {
			status = fmt.Sprintf("%s\u21E3", status)
		} else if matchStatus[1] == "ahead" {
			status = fmt.Sprintf("%s\u21E1", status)
		}
	}

	staged = !strings.Contains(string(stdout), "nothing to commit")
	if strings.Contains(string(stdout), "Untracked files") {
		status = fmt.Sprintf("%s +", status)
	}

	return status, staged
}

func addCwd(cwdParts []string, ellipsis string, separator string) [][]string {
	segments := [][]string{}
	home := false
	if cwdParts[0] == "~" {
		cwdParts = cwdParts[1:len(cwdParts)]
		home = true
	}

	if home {
		segments = append(segments, []string{"015", "031", "~"})

		if len(cwdParts) > 2 {
			segments = append(segments, []string{"250", "237", cwdParts[0], separator, "244"})
			segments = append(segments, []string{"250", "237", ellipsis, separator, "244"})
		} else if len(cwdParts) == 2 {
			segments = append(segments, []string{"250", "237", cwdParts[0], separator, "244"})
		}
	} else {
		if len(cwdParts[len(cwdParts)-1]) == 0 {
			segments = append(segments, []string{"250", "237", "/"})
		}

		if len(cwdParts) > 3 {
			segments = append(segments, []string{"250", "237", cwdParts[1], separator, "244"})
			segments = append(segments, []string{"250", "237", ellipsis, separator, "244"})
		} else if len(cwdParts) > 2 {
			segments = append(segments, []string{"250", "237", cwdParts[1], separator, "244"})
		}
	}

	if len(cwdParts) != 0 && len(cwdParts[len(cwdParts)-1]) > 0 {
		segments = append(segments, []string{"250", "237", cwdParts[len(cwdParts)-1]})
	}

	return segments
}

func addVirtulEnvName(virtualEnvName string) []string {
	if virtualEnvName != "" {
		return []string{"000", "035", virtualEnvName}
	}

	return nil
}

func addLock(cwd string, lock string) []string {
	if !isWritableDir(cwd) {
		return []string{"254", "124", lock}
	}

	return nil
}

func addGitInfo(status string, staged bool) []string {
	if status != "" {
		if staged {
			return []string{"015", "161", status}
		} else {
			return []string{"000", "148", status}
		}
	} else {
		return nil
	}
}

func addHostname(includeUsername bool) []string {
	hostname, err := os.Hostname()
	if err != nil {
		return nil
	}

	if includeUsername {
		user, err := user.Current()
		if err != nil {
			return nil
		}
		hostname = user.Username + "@" + hostname
	}
	return []string{"015", "161", hostname}
}

func addDollarPrompt() []string {
	return []string{"015", "236", "\\$"}
}

func main() {
	shell := "zsh"

	if len(os.Args) > 1 {
		shell = os.Args[1]
	}

	p := powerline.NewPowerline(shell)
	cwd, cwdParts := getCurrentWorkingDir()

	if _, found := syscall.Getenv("SSH_CLIENT"); found {
		p.AppendSegment(addHostname(true))
	}
	p.AppendSegments(addCwd(cwdParts, p.Ellipsis, p.SeparatorThin))
	p.AppendSegment(addVirtulEnvName(getVirtualEnv()))
	p.AppendSegment(addLock(cwd, p.Lock))
	p.AppendSegment(addGitInfo(getGitInformation()))
	p.AppendSegment(addDollarPrompt())

	fmt.Print(p.PrintSegments())
}
<commit_msg>Move virtual machine to beginning of prompt<commit_after>// Copyright 2014 Matt Martz <matt@sivel.net>
// All Rights Reserved.
//
//    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 main

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"os/user"
	"path"
	"path/filepath"
	"regexp"
	"strings"
	"syscall"

	"github.com/wm/powerline-shell-go/powerline"
)

func getCurrentWorkingDir() (string, []string) {
	dir, err := filepath.Abs(".")
	if err != nil {
		log.Fatal(err)
	}
	userDir := strings.Replace(dir, os.Getenv("HOME"), "~", 1)
	userDir = strings.TrimSuffix(userDir, "/")
	parts := strings.Split(userDir, "/")
	return dir, parts
}

func getVirtualEnv() string {
	virtualEnv := os.Getenv("VIRTUAL_ENV")
	if virtualEnv == "" {
		return ""
	}

	virtualEnvName := path.Base(virtualEnv)
	return virtualEnvName
}

func isWritableDir(dir string) bool {
	tmpPath := path.Join(dir, ".powerline-write-test")
	_, err := os.Create(tmpPath)
	if err != nil {
		return false
	}
	os.Remove(tmpPath)
	return true
}

func getGitInformation() (string, bool) {
	var status string
	var staged bool
	stdout, _ := exec.Command("git", "status", "--ignore-submodules").Output()
	reBranch := regexp.MustCompile(`^(HEAD detached at|HEAD detached from|# On branch|On branch) (\S+)`)
	matchBranch := reBranch.FindStringSubmatch(string(stdout))
	if len(matchBranch) > 0 {
		if matchBranch[2] == "detached" {
			status = matchBranch[2]
		} else {
			status = matchBranch[2]
		}
	}

	reStatus := regexp.MustCompile(`Your branch is (ahead|behind).*?([0-9]+) comm`)
	matchStatus := reStatus.FindStringSubmatch(string(stdout))
	if len(matchStatus) > 0 {
		status = fmt.Sprintf("%s %s", status, matchStatus[2])
		if matchStatus[1] == "behind" {
			status = fmt.Sprintf("%s\u21E3", status)
		} else if matchStatus[1] == "ahead" {
			status = fmt.Sprintf("%s\u21E1", status)
		}
	}

	staged = !strings.Contains(string(stdout), "nothing to commit")
	if strings.Contains(string(stdout), "Untracked files") {
		status = fmt.Sprintf("%s +", status)
	}

	return status, staged
}

func addCwd(cwdParts []string, ellipsis string, separator string) [][]string {
	segments := [][]string{}
	home := false
	if cwdParts[0] == "~" {
		cwdParts = cwdParts[1:len(cwdParts)]
		home = true
	}

	if home {
		segments = append(segments, []string{"015", "031", "~"})

		if len(cwdParts) > 2 {
			segments = append(segments, []string{"250", "237", cwdParts[0], separator, "244"})
			segments = append(segments, []string{"250", "237", ellipsis, separator, "244"})
		} else if len(cwdParts) == 2 {
			segments = append(segments, []string{"250", "237", cwdParts[0], separator, "244"})
		}
	} else {
		if len(cwdParts[len(cwdParts)-1]) == 0 {
			segments = append(segments, []string{"250", "237", "/"})
		}

		if len(cwdParts) > 3 {
			segments = append(segments, []string{"250", "237", cwdParts[1], separator, "244"})
			segments = append(segments, []string{"250", "237", ellipsis, separator, "244"})
		} else if len(cwdParts) > 2 {
			segments = append(segments, []string{"250", "237", cwdParts[1], separator, "244"})
		}
	}

	if len(cwdParts) != 0 && len(cwdParts[len(cwdParts)-1]) > 0 {
		segments = append(segments, []string{"250", "237", cwdParts[len(cwdParts)-1]})
	}

	return segments
}

func addVirtulEnvName(virtualEnvName string) []string {
	if virtualEnvName != "" {
		return []string{"000", "035", virtualEnvName}
	}

	return nil
}

func addLock(cwd string, lock string) []string {
	if !isWritableDir(cwd) {
		return []string{"254", "124", lock}
	}

	return nil
}

func addGitInfo(status string, staged bool) []string {
	if status != "" {
		if staged {
			return []string{"015", "161", status}
		} else {
			return []string{"000", "148", status}
		}
	} else {
		return nil
	}
}

func addHostname(includeUsername bool) []string {
	hostname, err := os.Hostname()
	if err != nil {
		return nil
	}

	if includeUsername {
		user, err := user.Current()
		if err != nil {
			return nil
		}
		hostname = user.Username + "@" + hostname
	}
	return []string{"015", "161", hostname}
}

func addDollarPrompt() []string {
	return []string{"015", "236", "\\$"}
}

func main() {
	shell := "zsh"

	if len(os.Args) > 1 {
		shell = os.Args[1]
	}

	p := powerline.NewPowerline(shell)
	cwd, cwdParts := getCurrentWorkingDir()

	p.AppendSegment(addVirtulEnvName(getVirtualEnv()))
	if _, found := syscall.Getenv("SSH_CLIENT"); found {
		p.AppendSegment(addHostname(true))
	}
	p.AppendSegments(addCwd(cwdParts, p.Ellipsis, p.SeparatorThin))
	p.AppendSegment(addLock(cwd, p.Lock))
	p.AppendSegment(addGitInfo(getGitInformation()))
	p.AppendSegment(addDollarPrompt())

	fmt.Print(p.PrintSegments())
}
<|endoftext|>


<commit_before>// Copyright 2014 Matt Martz <matt@sivel.net>
// All Rights Reserved.
//
//    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 main

import (
	"bytes"
	"fmt"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"regexp"
	"strings"
)

func getCurrentWorkingDir() (string, []string) {
	dir, err := filepath.Abs(".")
	if err != nil {
		log.Fatal(err)
	}
	userDir := strings.Replace(dir, os.Getenv("HOME"), "~", 1)
	parts := strings.Split(userDir, "/")
	return dir, parts
}

func getVirtualEnv() (string, []string, string) {
	var parts []string
	virtualEnv := os.Getenv("VIRTUAL_ENV")
	if virtualEnv == "" {
		return "", parts, ""
	}

	parts = strings.Split(virtualEnv, "/")

	virtualEnvName := path.Base(virtualEnv)
	return virtualEnv, parts, virtualEnvName
}

func isWritableDir(dir string) bool {
	tmpPath := path.Join(dir, ".powerline-write-test")
	_, err := os.Create(tmpPath)
	if err != nil {
		return false
	}
	os.Remove(tmpPath)
	return true
}

func getGitInformation() (string, bool) {
	var status string
	var staged bool
	stdout, _ := exec.Command("git", "status", "--ignore-submodules").Output()
	reBranch := regexp.MustCompile(`^(HEAD|On branch) (\S+)`)
	matchBranch := reBranch.FindStringSubmatch(string(stdout))
	if len(matchBranch) > 0 {
		if matchBranch[2] == "detached" {
			status = "(Detached)"
		} else {
			status = matchBranch[2]
		}
	}

	reStatus := regexp.MustCompile(`Your branch is (ahead|behind).*?([0-9]+) comm`)
	matchStatus := reStatus.FindStringSubmatch(string(stdout))
	if len(matchStatus) > 0 {
		status = fmt.Sprintf("%s %s", status, matchStatus[2])
		if matchStatus[1] == "behind" {
			status = fmt.Sprintf("%s\u21E3", status)
		} else if matchStatus[1] == "ahead" {
			status = fmt.Sprintf("%s\u21E1", status)
		}
	}

	staged = !strings.Contains(string(stdout), "nothing to commit")
	if strings.Contains(string(stdout), "Untracked files") {
		status = fmt.Sprintf("%s +", status)
	}

	return status, staged
}

type Powerline struct {
	BashTemplate  string
	ColorTemplate string
	Reset         string
	Lock          string
	Network       string
	Separator     string
	SeparatorThin string
	Ellipsis      string
	Segments      [][]string
}

func (p *Powerline) Segment(content string, fg string, bg string) string {
	foreground := fmt.Sprintf(p.BashTemplate, fmt.Sprintf(p.ColorTemplate, "38", fg))
	background := fmt.Sprintf(p.BashTemplate, fmt.Sprintf(p.ColorTemplate, "48", bg))
	return fmt.Sprintf("%s%s %s", foreground, background, content)
}

func (p *Powerline) Color(prefix string, code string) string {
	return fmt.Sprintf(p.BashTemplate, fmt.Sprintf(p.ColorTemplate, prefix, code))
}

func (p *Powerline) ForegroundColor(code string) string {
	return p.Color("38", code)
}

func (p *Powerline) BackgroundColor(code string) string {
	return p.Color("48", code)
}

func (p *Powerline) PrintSegments() string {
	var nextBackground string
	var buffer bytes.Buffer
	for i, Segment := range p.Segments {
		if (i + 1) == len(p.Segments) {
			nextBackground = p.Reset
		} else {
			nextBackground = p.BackgroundColor(p.Segments[i+1][1])
		}
		if len(Segment) == 3 {
			buffer.WriteString(fmt.Sprintf("%s%s %s %s%s%s", p.ForegroundColor(Segment[0]), p.BackgroundColor(Segment[1]), Segment[2], nextBackground, p.ForegroundColor(Segment[1]), p.Separator))
		} else {
			buffer.WriteString(fmt.Sprintf("%s%s %s %s%s%s", p.ForegroundColor(Segment[0]), p.BackgroundColor(Segment[1]), Segment[2], nextBackground, p.ForegroundColor(Segment[4]), Segment[3]))
		}
	}

	buffer.WriteString(p.Reset)

	return buffer.String()
}

func main() {
	home := false
	p := Powerline{
		BashTemplate:  "\\[\\e%s\\]",
		ColorTemplate: "[%s;5;%sm",
		Reset:         "\\[\\e[0m\\]",
		Lock:          "\uE0A2",
		Network:       "\uE0A2",
		Separator:     "\uE0B0",
		SeparatorThin: "\uE0B1",
		Ellipsis:      "\u2026",
	}
	cwd, cwdParts := getCurrentWorkingDir()
	_, _, virtualEnvName := getVirtualEnv()
	if virtualEnvName != "" {
		p.Segments = append(p.Segments, []string{"00", "35", virtualEnvName})
	}
	if cwdParts[0] == "~" {
		cwdParts = cwdParts[1:len(cwdParts)]
		p.Segments = append(p.Segments, []string{"15", "31", "~"})
		home = true
	}
	if len(cwdParts) >= 4 {
		p.Segments = append(p.Segments, []string{"250", "237", cwdParts[0], p.SeparatorThin, "244"})
		p.Segments = append(p.Segments, []string{"250", "237", p.Ellipsis, p.SeparatorThin, "244"})
		p.Segments = append(p.Segments, []string{"254", "237", cwdParts[len(cwdParts)-1]})
	} else if len(cwdParts) == 3 {
		if home {
			p.Segments = append(p.Segments, []string{"250", "237", cwdParts[0], p.SeparatorThin, "244"})
		} else {
			p.Segments = append(p.Segments, []string{"250", "237", cwdParts[1], p.SeparatorThin, "244"})
		}
		p.Segments = append(p.Segments, []string{"250", "237", p.Ellipsis, p.SeparatorThin, "244"})
		p.Segments = append(p.Segments, []string{"254", "237", cwdParts[len(cwdParts)-1]})
	} else if len(cwdParts) != 0 {
		p.Segments = append(p.Segments, []string{"254", "237", cwdParts[len(cwdParts)-1]})
	}

	if !isWritableDir(cwd) {
		p.Segments = append(p.Segments, []string{"254", "124", p.Lock})
	}

	gitStatus, gitStaged := getGitInformation()
	if gitStatus != "" {
		if gitStaged {
			p.Segments = append(p.Segments, []string{"15", "161", gitStatus})
		} else {
			p.Segments = append(p.Segments, []string{"0", "148", gitStatus})
		}
	}

	p.Segments = append(p.Segments, []string{"15", "236", "\\$"})

	fmt.Println(p.PrintSegments())
}
<commit_msg>Make sure we print the correct slice of the cwd<commit_after>// Copyright 2014 Matt Martz <matt@sivel.net>
// All Rights Reserved.
//
//    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 main

import (
	"bytes"
	"fmt"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"regexp"
	"strings"
)

func getCurrentWorkingDir() (string, []string) {
	dir, err := filepath.Abs(".")
	if err != nil {
		log.Fatal(err)
	}
	userDir := strings.Replace(dir, os.Getenv("HOME"), "~", 1)
	parts := strings.Split(userDir, "/")
	return dir, parts
}

func getVirtualEnv() (string, []string, string) {
	var parts []string
	virtualEnv := os.Getenv("VIRTUAL_ENV")
	if virtualEnv == "" {
		return "", parts, ""
	}

	parts = strings.Split(virtualEnv, "/")

	virtualEnvName := path.Base(virtualEnv)
	return virtualEnv, parts, virtualEnvName
}

func isWritableDir(dir string) bool {
	tmpPath := path.Join(dir, ".powerline-write-test")
	_, err := os.Create(tmpPath)
	if err != nil {
		return false
	}
	os.Remove(tmpPath)
	return true
}

func getGitInformation() (string, bool) {
	var status string
	var staged bool
	stdout, _ := exec.Command("git", "status", "--ignore-submodules").Output()
	reBranch := regexp.MustCompile(`^(HEAD|On branch) (\S+)`)
	matchBranch := reBranch.FindStringSubmatch(string(stdout))
	if len(matchBranch) > 0 {
		if matchBranch[2] == "detached" {
			status = "(Detached)"
		} else {
			status = matchBranch[2]
		}
	}

	reStatus := regexp.MustCompile(`Your branch is (ahead|behind).*?([0-9]+) comm`)
	matchStatus := reStatus.FindStringSubmatch(string(stdout))
	if len(matchStatus) > 0 {
		status = fmt.Sprintf("%s %s", status, matchStatus[2])
		if matchStatus[1] == "behind" {
			status = fmt.Sprintf("%s\u21E3", status)
		} else if matchStatus[1] == "ahead" {
			status = fmt.Sprintf("%s\u21E1", status)
		}
	}

	staged = !strings.Contains(string(stdout), "nothing to commit")
	if strings.Contains(string(stdout), "Untracked files") {
		status = fmt.Sprintf("%s +", status)
	}

	return status, staged
}

type Powerline struct {
	BashTemplate  string
	ColorTemplate string
	Reset         string
	Lock          string
	Network       string
	Separator     string
	SeparatorThin string
	Ellipsis      string
	Segments      [][]string
}

func (p *Powerline) Segment(content string, fg string, bg string) string {
	foreground := fmt.Sprintf(p.BashTemplate, fmt.Sprintf(p.ColorTemplate, "38", fg))
	background := fmt.Sprintf(p.BashTemplate, fmt.Sprintf(p.ColorTemplate, "48", bg))
	return fmt.Sprintf("%s%s %s", foreground, background, content)
}

func (p *Powerline) Color(prefix string, code string) string {
	return fmt.Sprintf(p.BashTemplate, fmt.Sprintf(p.ColorTemplate, prefix, code))
}

func (p *Powerline) ForegroundColor(code string) string {
	return p.Color("38", code)
}

func (p *Powerline) BackgroundColor(code string) string {
	return p.Color("48", code)
}

func (p *Powerline) PrintSegments() string {
	var nextBackground string
	var buffer bytes.Buffer
	for i, Segment := range p.Segments {
		if (i + 1) == len(p.Segments) {
			nextBackground = p.Reset
		} else {
			nextBackground = p.BackgroundColor(p.Segments[i+1][1])
		}
		if len(Segment) == 3 {
			buffer.WriteString(fmt.Sprintf("%s%s %s %s%s%s", p.ForegroundColor(Segment[0]), p.BackgroundColor(Segment[1]), Segment[2], nextBackground, p.ForegroundColor(Segment[1]), p.Separator))
		} else {
			buffer.WriteString(fmt.Sprintf("%s%s %s %s%s%s", p.ForegroundColor(Segment[0]), p.BackgroundColor(Segment[1]), Segment[2], nextBackground, p.ForegroundColor(Segment[4]), Segment[3]))
		}
	}

	buffer.WriteString(p.Reset)

	return buffer.String()
}

func main() {
	home := false
	p := Powerline{
		BashTemplate:  "\\[\\e%s\\]",
		ColorTemplate: "[%s;5;%sm",
		Reset:         "\\[\\e[0m\\]",
		Lock:          "\uE0A2",
		Network:       "\uE0A2",
		Separator:     "\uE0B0",
		SeparatorThin: "\uE0B1",
		Ellipsis:      "\u2026",
	}
	cwd, cwdParts := getCurrentWorkingDir()
	_, _, virtualEnvName := getVirtualEnv()
	if virtualEnvName != "" {
		p.Segments = append(p.Segments, []string{"00", "35", virtualEnvName})
	}
	if cwdParts[0] == "~" {
		cwdParts = cwdParts[1:len(cwdParts)]
		p.Segments = append(p.Segments, []string{"15", "31", "~"})
		home = true
	}
	if len(cwdParts) >= 4 {
		p.Segments = append(p.Segments, []string{"250", "237", cwdParts[1], p.SeparatorThin, "244"})
		p.Segments = append(p.Segments, []string{"250", "237", p.Ellipsis, p.SeparatorThin, "244"})
		p.Segments = append(p.Segments, []string{"254", "237", cwdParts[len(cwdParts)-1]})
	} else if len(cwdParts) == 3 {
		if home {
			p.Segments = append(p.Segments, []string{"250", "237", cwdParts[0], p.SeparatorThin, "244"})
		} else {
			p.Segments = append(p.Segments, []string{"250", "237", cwdParts[1], p.SeparatorThin, "244"})
		}
		p.Segments = append(p.Segments, []string{"250", "237", p.Ellipsis, p.SeparatorThin, "244"})
		p.Segments = append(p.Segments, []string{"254", "237", cwdParts[len(cwdParts)-1]})
	} else if len(cwdParts) != 0 {
		p.Segments = append(p.Segments, []string{"254", "237", cwdParts[len(cwdParts)-1]})
	}

	if !isWritableDir(cwd) {
		p.Segments = append(p.Segments, []string{"254", "124", p.Lock})
	}

	gitStatus, gitStaged := getGitInformation()
	if gitStatus != "" {
		if gitStaged {
			p.Segments = append(p.Segments, []string{"15", "161", gitStatus})
		} else {
			p.Segments = append(p.Segments, []string{"0", "148", gitStatus})
		}
	}

	p.Segments = append(p.Segments, []string{"15", "236", "\\$"})

	fmt.Println(p.PrintSegments())
}
<|endoftext|>