<commit_before>package tview

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

// The states of the ANSI escape code parser.
const (
	ansiText = iota
	ansiEscape
	ansiSubstring
	ansiControlSequence
)

// ansi is a io.Writer which translates ANSI escape codes into tview color
// tags.
type ansi struct {
	io.Writer

	// Reusable buffers.
	buffer                        *bytes.Buffer // The entire output text of one Write().
	csiParameter, csiIntermediate *bytes.Buffer // Partial CSI strings.

	// The current state of the parser. One of the ansi constants.
	state int
}

// ANSIWriter returns an io.Writer which translates any ANSI escape codes
// written to it into tview color tags. Other escape codes don't have an effect
// and are simply removed. The translated text is written to the provided
// writer.
func ANSIWriter(writer io.Writer) io.Writer {
	return &ansi{
		Writer:          writer,
		buffer:          new(bytes.Buffer),
		csiParameter:    new(bytes.Buffer),
		csiIntermediate: new(bytes.Buffer),
		state:           ansiText,
	}
}

// Write parses the given text as a string of runes, translates ANSI escape
// codes to color tags and writes them to the output writer.
func (a *ansi) Write(text []byte) (int, error) {
	defer func() {
		a.buffer.Reset()
	}()

	for _, r := range string(text) {
		switch a.state {

		// We just entered an escape sequence.
		case ansiEscape:
			switch r {
			case '[': // Control Sequence Introducer.
				a.csiParameter.Reset()
				a.csiIntermediate.Reset()
				a.state = ansiControlSequence
			case 'c': // Reset.
				fmt.Fprint(a.buffer, "[-:-:-]")
				a.state = ansiText
			case 'P', ']', 'X', '^', '_': // Substrings and commands.
				a.state = ansiSubstring
			default: // Ignore.
				a.state = ansiText
			}

		// CSI Sequences.
		case ansiControlSequence:
			switch {
			case r >= 0x30 && r <= 0x3f: // Parameter bytes.
				if _, err := a.csiParameter.WriteRune(r); err != nil {
					return 0, err
				}
			case r >= 0x20 && r <= 0x2f: // Intermediate bytes.
				if _, err := a.csiIntermediate.WriteRune(r); err != nil {
					return 0, err
				}
			case r >= 0x40 && r <= 0x7e: // Final byte.
				switch r {
				case 'E': // Next line.
					count, _ := strconv.Atoi(a.csiParameter.String())
					if count == 0 {
						count = 1
					}
					fmt.Fprint(a.buffer, strings.Repeat("\n", count))
				case 'm': // Select Graphic Rendition.
					var (
						background, foreground, attributes string
						clearAttributes                    bool
					)
					fields := strings.Split(a.csiParameter.String(), ";")
					if len(fields) == 0 || len(fields) == 1 && fields[0] == "0" {
						// Reset.
						if _, err := a.buffer.WriteString("[-:-:-]"); err != nil {
							return 0, err
						}
						break
					}
					lookupColor := func(colorNumber int, bright bool) string {
						if colorNumber < 0 || colorNumber > 7 {
							return "black"
						}
						if bright {
							colorNumber += 8
						}
						return [...]string{
							"black",
							"maroon",
							"green",
							"olive",
							"navy",
							"purple",
							"teal",
							"silver",
							"gray",
							"red",
							"lime",
							"yellow",
							"blue",
							"fuchsia",
							"aqua",
							"white",
						}[colorNumber]
					}
				FieldLoop:
					for index, field := range fields {
						switch field {
						case "1", "01":
							attributes += "b"
						case "2", "02":
							attributes += "d"
						case "4", "04":
							attributes += "u"
						case "5", "05":
							attributes += "l"
						case "7", "07":
							attributes += "7"
						case "22", "24", "25", "27":
							clearAttributes = true
						case "30", "31", "32", "33", "34", "35", "36", "37":
							colorNumber, _ := strconv.Atoi(field)
							foreground = lookupColor(colorNumber-30, false)
						case "39":
							foreground = "-"
						case "40", "41", "42", "43", "44", "45", "46", "47":
							colorNumber, _ := strconv.Atoi(field)
							background = lookupColor(colorNumber-40, false)
						case "49":
							background = "-"
						case "90", "91", "92", "93", "94", "95", "96", "97":
							colorNumber, _ := strconv.Atoi(field)
							foreground = lookupColor(colorNumber-90, true)
						case "100", "101", "102", "103", "104", "105", "106", "107":
							colorNumber, _ := strconv.Atoi(field)
							background = lookupColor(colorNumber-100, true)
						case "38", "48":
							var color string
							if len(fields) > index+1 {
								if fields[index+1] == "5" && len(fields) > index+2 { // 8-bit colors.
									colorNumber, _ := strconv.Atoi(fields[index+2])
									if colorNumber <= 7 {
										color = lookupColor(colorNumber, false)
									} else if colorNumber <= 15 {
										color = lookupColor(colorNumber, true)
									} else if colorNumber <= 231 {
										red := (colorNumber - 16) / 36
										green := ((colorNumber - 16) / 6) % 6
										blue := (colorNumber - 16) % 6
										color = fmt.Sprintf("#%02x%02x%02x", 255*red/5, 255*green/5, 255*blue/5)
									} else if colorNumber <= 255 {
										grey := 255 * (colorNumber - 232) / 23
										color = fmt.Sprintf("#%02x%02x%02x", grey, grey, grey)
									}
								} else if fields[index+1] == "2" && len(fields) > index+4 { // 24-bit colors.
									red, _ := strconv.Atoi(fields[index+2])
									green, _ := strconv.Atoi(fields[index+3])
									blue, _ := strconv.Atoi(fields[index+4])
									color = fmt.Sprintf("#%02x%02x%02x", red, green, blue)
								}
							}
							if len(color) > 0 {
								if field == "38" {
									foreground = color
								} else {
									background = color
								}
							}
							break FieldLoop
						}
					}
					if len(attributes) > 0 || clearAttributes {
						attributes = ":" + attributes
					}
					if len(foreground) > 0 || len(background) > 0 || len(attributes) > 0 {
						fmt.Fprintf(a.buffer, "[%s:%s%s]", foreground, background, attributes)
					}
				}
				a.state = ansiText
			default: // Undefined byte.
				a.state = ansiText // Abort CSI.
			}

			// We just entered a substring/command sequence.
		case ansiSubstring:
			if r == 27 { // Most likely the end of the substring.
				a.state = ansiEscape
			} // Ignore all other characters.

			// "ansiText" and all others.
		default:
			if r == 27 {
				// This is the start of an escape sequence.
				a.state = ansiEscape
			} else {
				// Just a regular rune. Send to buffer.
				if _, err := a.buffer.WriteRune(r); err != nil {
					return 0, err
				}
			}
		}
	}

	// Write buffer to target writer.
	n, err := a.buffer.WriteTo(a.Writer)
	if err != nil {
		return int(n), err
	}
	return len(text), nil
}

// TranslateANSI replaces ANSI escape sequences found in the provided string
// with tview's color tags and returns the resulting string.
func TranslateANSI(text string) string {
	var buffer bytes.Buffer
	writer := ANSIWriter(&buffer)
	writer.Write([]byte(text))
	return buffer.String()
}
<commit_msg>Fixed 8-bit background colors<commit_after>package tview

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

// The states of the ANSI escape code parser.
const (
	ansiText = iota
	ansiEscape
	ansiSubstring
	ansiControlSequence
)

// ansi is a io.Writer which translates ANSI escape codes into tview color
// tags.
type ansi struct {
	io.Writer

	// Reusable buffers.
	buffer                        *bytes.Buffer // The entire output text of one Write().
	csiParameter, csiIntermediate *bytes.Buffer // Partial CSI strings.

	// The current state of the parser. One of the ansi constants.
	state int
}

// ANSIWriter returns an io.Writer which translates any ANSI escape codes
// written to it into tview color tags. Other escape codes don't have an effect
// and are simply removed. The translated text is written to the provided
// writer.
func ANSIWriter(writer io.Writer) io.Writer {
	return &ansi{
		Writer:          writer,
		buffer:          new(bytes.Buffer),
		csiParameter:    new(bytes.Buffer),
		csiIntermediate: new(bytes.Buffer),
		state:           ansiText,
	}
}

// Write parses the given text as a string of runes, translates ANSI escape
// codes to color tags and writes them to the output writer.
func (a *ansi) Write(text []byte) (int, error) {
	defer func() {
		a.buffer.Reset()
	}()

	for _, r := range string(text) {
		switch a.state {

		// We just entered an escape sequence.
		case ansiEscape:
			switch r {
			case '[': // Control Sequence Introducer.
				a.csiParameter.Reset()
				a.csiIntermediate.Reset()
				a.state = ansiControlSequence
			case 'c': // Reset.
				fmt.Fprint(a.buffer, "[-:-:-]")
				a.state = ansiText
			case 'P', ']', 'X', '^', '_': // Substrings and commands.
				a.state = ansiSubstring
			default: // Ignore.
				a.state = ansiText
			}

		// CSI Sequences.
		case ansiControlSequence:
			switch {
			case r >= 0x30 && r <= 0x3f: // Parameter bytes.
				if _, err := a.csiParameter.WriteRune(r); err != nil {
					return 0, err
				}
			case r >= 0x20 && r <= 0x2f: // Intermediate bytes.
				if _, err := a.csiIntermediate.WriteRune(r); err != nil {
					return 0, err
				}
			case r >= 0x40 && r <= 0x7e: // Final byte.
				switch r {
				case 'E': // Next line.
					count, _ := strconv.Atoi(a.csiParameter.String())
					if count == 0 {
						count = 1
					}
					fmt.Fprint(a.buffer, strings.Repeat("\n", count))
				case 'm': // Select Graphic Rendition.
					var (
						background, foreground, attributes string
						clearAttributes                    bool
					)
					fields := strings.Split(a.csiParameter.String(), ";")
					if len(fields) == 0 || len(fields) == 1 && fields[0] == "0" {
						// Reset.
						if _, err := a.buffer.WriteString("[-:-:-]"); err != nil {
							return 0, err
						}
						break
					}
					lookupColor := func(colorNumber int) string {
						if colorNumber < 0 || colorNumber > 15 {
							return "black"
						}
						return []string{
							"black",
							"maroon",
							"green",
							"olive",
							"navy",
							"purple",
							"teal",
							"silver",
							"gray",
							"red",
							"lime",
							"yellow",
							"blue",
							"fuchsia",
							"aqua",
							"white",
						}[colorNumber]
					}
				FieldLoop:
					for index, field := range fields {
						switch field {
						case "1", "01":
							attributes += "b"
						case "2", "02":
							attributes += "d"
						case "4", "04":
							attributes += "u"
						case "5", "05":
							attributes += "l"
						case "7", "07":
							attributes += "7"
						case "22", "24", "25", "27":
							clearAttributes = true
						case "30", "31", "32", "33", "34", "35", "36", "37":
							colorNumber, _ := strconv.Atoi(field)
							foreground = lookupColor(colorNumber-30)
						case "39":
							foreground = "-"
						case "40", "41", "42", "43", "44", "45", "46", "47":
							colorNumber, _ := strconv.Atoi(field)
							background = lookupColor(colorNumber-40)
						case "49":
							background = "-"
						case "90", "91", "92", "93", "94", "95", "96", "97":
							colorNumber, _ := strconv.Atoi(field)
							foreground = lookupColor(colorNumber-82)
						case "100", "101", "102", "103", "104", "105", "106", "107":
							colorNumber, _ := strconv.Atoi(field)
							background = lookupColor(colorNumber-92)
						case "38", "48":
							var color string
							if len(fields) > index+1 {
								if fields[index+1] == "5" && len(fields) > index+2 { // 8-bit colors.
									colorNumber, _ := strconv.Atoi(fields[index+2])
									if colorNumber <= 15 {
										color = lookupColor(colorNumber)
									} else if colorNumber <= 231 {
										red := (colorNumber - 16) / 36
										green := ((colorNumber - 16) / 6) % 6
										blue := (colorNumber - 16) % 6
										color = fmt.Sprintf("#%02x%02x%02x", 255*red/5, 255*green/5, 255*blue/5)
									} else if colorNumber <= 255 {
										grey := 255 * (colorNumber - 232) / 23
										color = fmt.Sprintf("#%02x%02x%02x", grey, grey, grey)
									}
								} else if fields[index+1] == "2" && len(fields) > index+4 { // 24-bit colors.
									red, _ := strconv.Atoi(fields[index+2])
									green, _ := strconv.Atoi(fields[index+3])
									blue, _ := strconv.Atoi(fields[index+4])
									color = fmt.Sprintf("#%02x%02x%02x", red, green, blue)
								}
							}
							if len(color) > 0 {
								if field == "38" {
									foreground = color
								} else {
									background = color
								}
							}
							break FieldLoop
						}
					}
					if len(attributes) > 0 || clearAttributes {
						attributes = ":" + attributes
					}
					if len(foreground) > 0 || len(background) > 0 || len(attributes) > 0 {
						fmt.Fprintf(a.buffer, "[%s:%s%s]", foreground, background, attributes)
					}
				}
				a.state = ansiText
			default: // Undefined byte.
				a.state = ansiText // Abort CSI.
			}

			// We just entered a substring/command sequence.
		case ansiSubstring:
			if r == 27 { // Most likely the end of the substring.
				a.state = ansiEscape
			} // Ignore all other characters.

			// "ansiText" and all others.
		default:
			if r == 27 {
				// This is the start of an escape sequence.
				a.state = ansiEscape
			} else {
				// Just a regular rune. Send to buffer.
				if _, err := a.buffer.WriteRune(r); err != nil {
					return 0, err
				}
			}
		}
	}

	// Write buffer to target writer.
	n, err := a.buffer.WriteTo(a.Writer)
	if err != nil {
		return int(n), err
	}
	return len(text), nil
}

// TranslateANSI replaces ANSI escape sequences found in the provided string
// with tview's color tags and returns the resulting string.
func TranslateANSI(text string) string {
	var buffer bytes.Buffer
	writer := ANSIWriter(&buffer)
	writer.Write([]byte(text))
	return buffer.String()
}
<|endoftext|>


<commit_before>package resolves

import (
	"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"

	client "github.com/wanliu/flow/builtin/graphql_client"
)

// 处理开单的逻辑结构, 不需要是组件
// 作为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 = database.NewOrderItem("", pr.Product, pr.Product, pr.Price)
		items = append(items, item)
	}

	for _, pr := range r.Gifts.Products {
		gift = database.NewGiftItem("", pr.Product, pr.Quantity)
		gifts = append(items, gift)
	}

	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.No, order.ID)
	}
}

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://wanliu.biz/orders/" + 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>create order for user<commit_after>package resolves

import (
	"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, pr.Product, pr.Price)
		if err != nil {
			return err.Error()
		}
		items = append(items, item)
	}

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

		gifts = append(gifts, gift)
	}

	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.No, order.ID)
	}
}

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://wanliu.biz/orders/" + 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 apex provides Lambda support for Go via a
// Node.js shim and this package for operating over
// stdio.
package apex

import (
	"encoding/json"
	"io"
	"log"
	"os"
	"sync"
)

// Handler handles Lambda events.
type Handler interface {
	Handle(json.RawMessage, *Context) (interface{}, error)
}

// HandlerFunc implements Handler.
type HandlerFunc func(json.RawMessage, *Context) (interface{}, error)

// Handle Lambda event.
func (h HandlerFunc) Handle(event json.RawMessage, ctx *Context) (interface{}, error) {
	return h(event, ctx)
}

// Context represents the context data provided by a Lambda invocation.
type Context struct {
	InvokeID                 string          `json:"invokeid"`
	RequestID                string          `json:"awsRequestId"`
	FunctionName             string          `json:"functionName"`
	FunctionVersion          string          `json:"functionVersion"`
	LogGroupName             string          `json:"logGroupName"`
	LogStreamName            string          `json:"logStreamName"`
	MemoryLimitInMB          string          `json:"memoryLimitInMB"`
	IsDefaultFunctionVersion bool            `json:"isDefaultFunctionVersion"`
	ClientContext            json.RawMessage `json:"clientContext"`
}

// Handle Lambda events with the given handler.
func Handle(h Handler) {
	m := &manager{
		Reader:  os.Stdin,
		Writer:  os.Stdout,
		Handler: h,
	}

	m.Start()
}

// HandleFunc handles Lambda events with the given handler function.
func HandleFunc(h HandlerFunc) {
	Handle(h)
}

// input for the node shim.
type input struct {
	Event   json.RawMessage `json:"event"`
	Context *Context        `json:"context"`
}

// output from the node shim.
type output struct {
	Error string      `json:"error,omitempty"`
	Value interface{} `json:"value,omitempty"`
}

// manager for operating over stdio.
type manager struct {
	Reader  io.Reader
	Writer  io.Writer
	Handler Handler
}

// Start the manager.
func (m *manager) Start() {
	m.output(m.handle(m.input()))
}

// input reads from the Reader and decodes JSON messages.
func (m *manager) input() <-chan *input {
	dec := json.NewDecoder(m.Reader)
	ch := make(chan *input)

	go func() {
		defer close(ch)

		for {
			msg := new(input)
			err := dec.Decode(msg)

			if err == io.EOF {
				break
			}

			if err != nil {
				log.Printf("error decoding input: %s", err)
				break
			}

			ch <- msg
		}
	}()

	return ch
}

// handle invokes the handler and sends the response to the output channel
func (m *manager) handle(in <-chan *input) <-chan *output {
	ch := make(chan *output)
	var wg sync.WaitGroup

	go func() {
		defer close(ch)

		for msg := range in {
			msg := msg
			wg.Add(1)

			go func() {
				defer wg.Done()
				ch <- m.invoke(msg)
			}()
		}

		wg.Wait()
	}()

	return ch
}

// invoke calls the handler with `msg`.
func (m *manager) invoke(msg *input) *output {
	v, err := m.Handler.Handle(msg.Event, msg.Context)

	if err != nil {
		return &output{Error: err.Error()}
	}

	return &output{Value: v}
}

// output encodes the JSON messages and writes to the Writer.
func (m *manager) output(ch <-chan *output) {
	enc := json.NewEncoder(m.Writer)

	for msg := range ch {
		if err := enc.Encode(msg); err != nil {
			log.Printf("error encoding output: %s", err)
		}
	}
}
<commit_msg>Add Identity to Context to support gathering cognito identities<commit_after>// Package apex provides Lambda support for Go via a
// Node.js shim and this package for operating over
// stdio.
package apex

import (
	"encoding/json"
	"io"
	"log"
	"os"
	"sync"
)

// Handler handles Lambda events.
type Handler interface {
	Handle(json.RawMessage, *Context) (interface{}, error)
}

// HandlerFunc implements Handler.
type HandlerFunc func(json.RawMessage, *Context) (interface{}, error)

// Handle Lambda event.
func (h HandlerFunc) Handle(event json.RawMessage, ctx *Context) (interface{}, error) {
	return h(event, ctx)
}

// Context represents the context data provided by a Lambda invocation.
type Context struct {
	InvokeID                 string          `json:"invokeid"`
	RequestID                string          `json:"awsRequestId"`
	FunctionName             string          `json:"functionName"`
	FunctionVersion          string          `json:"functionVersion"`
	LogGroupName             string          `json:"logGroupName"`
	LogStreamName            string          `json:"logStreamName"`
	MemoryLimitInMB          string          `json:"memoryLimitInMB"`
	IsDefaultFunctionVersion bool            `json:"isDefaultFunctionVersion"`
	ClientContext            json.RawMessage `json:"clientContext"`
	Identity                 Identity        `json:"identity,omitempty"`
}

// As defined in: http://docs.aws.amazon.com/mobile/sdkforandroid/developerguide/lambda.html#identity-context
type Identity struct {
	CognitoIdentityId       string `json:"cognitoIdentityId"`
	CognitoIdentityIdPoolId string `json:"cognitoIdentityPoolId"`
}

// Handle Lambda events with the given handler.
func Handle(h Handler) {
	m := &manager{
		Reader:  os.Stdin,
		Writer:  os.Stdout,
		Handler: h,
	}

	m.Start()
}

// HandleFunc handles Lambda events with the given handler function.
func HandleFunc(h HandlerFunc) {
	Handle(h)
}

// input for the node shim.
type input struct {
	Event   json.RawMessage `json:"event"`
	Context *Context        `json:"context"`
}

// output from the node shim.
type output struct {
	Error string      `json:"error,omitempty"`
	Value interface{} `json:"value,omitempty"`
}

// manager for operating over stdio.
type manager struct {
	Reader  io.Reader
	Writer  io.Writer
	Handler Handler
}

// Start the manager.
func (m *manager) Start() {
	m.output(m.handle(m.input()))
}

// input reads from the Reader and decodes JSON messages.
func (m *manager) input() <-chan *input {
	dec := json.NewDecoder(m.Reader)
	ch := make(chan *input)

	go func() {
		defer close(ch)

		for {
			msg := new(input)
			err := dec.Decode(msg)

			if err == io.EOF {
				break
			}

			if err != nil {
				log.Printf("error decoding input: %s", err)
				break
			}

			ch <- msg
		}
	}()

	return ch
}

// handle invokes the handler and sends the response to the output channel
func (m *manager) handle(in <-chan *input) <-chan *output {
	ch := make(chan *output)
	var wg sync.WaitGroup

	go func() {
		defer close(ch)

		for msg := range in {
			msg := msg
			wg.Add(1)

			go func() {
				defer wg.Done()
				ch <- m.invoke(msg)
			}()
		}

		wg.Wait()
	}()

	return ch
}

// invoke calls the handler with `msg`.
func (m *manager) invoke(msg *input) *output {
	v, err := m.Handler.Handle(msg.Event, msg.Context)

	if err != nil {
		return &output{Error: err.Error()}
	}

	return &output{Value: v}
}

// output encodes the JSON messages and writes to the Writer.
func (m *manager) output(ch <-chan *output) {
	enc := json.NewEncoder(m.Writer)

	for msg := range ch {
		if err := enc.Encode(msg); err != nil {
			log.Printf("error encoding output: %s", err)
		}
	}
}
<|endoftext|>


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

// +build !noconntrack

package collector

import (
	"errors"
	"fmt"
	"os"

	"github.com/go-kit/log"
	"github.com/go-kit/log/level"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/procfs"
)

type conntrackCollector struct {
	current       *prometheus.Desc
	limit         *prometheus.Desc
	found         *prometheus.Desc
	invalid       *prometheus.Desc
	ignore        *prometheus.Desc
	insert        *prometheus.Desc
	insertFailed  *prometheus.Desc
	drop          *prometheus.Desc
	earlyDrop     *prometheus.Desc
	searchRestart *prometheus.Desc
	logger        log.Logger
}

type conntrackStatistics struct {
	found         uint64 // Number of searched entries which were successful
	invalid       uint64 // Number of packets seen which can not be tracked
	ignore        uint64 // Number of packets seen which are already connected to a conntrack entry
	insert        uint64 // Number of entries inserted into the list
	insertFailed  uint64 // Number of entries for which list insertion was attempted but failed (happens if the same entry is already present)
	drop          uint64 // Number of packets dropped due to conntrack failure. Either new conntrack entry allocation failed, or protocol helper dropped the packet
	earlyDrop     uint64 // Number of dropped conntrack entries to make room for new ones, if maximum table size was reached
	searchRestart uint64 // Number of conntrack table lookups which had to be restarted due to hashtable resizes
}

func init() {
	registerCollector("conntrack", defaultEnabled, NewConntrackCollector)
}

// NewConntrackCollector returns a new Collector exposing conntrack stats.
func NewConntrackCollector(logger log.Logger) (Collector, error) {
	return &conntrackCollector{
		current: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, "", "nf_conntrack_entries"),
			"Number of currently allocated flow entries for connection tracking.",
			nil, nil,
		),
		limit: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, "", "nf_conntrack_entries_limit"),
			"Maximum size of connection tracking table.",
			nil, nil,
		),
		found: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, "", "nf_conntrack_stat_found"),
			"Number of searched entries which were successful.",
			nil, nil,
		),
		invalid: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, "", "nf_conntrack_stat_invalid"),
			"Number of packets seen which can not be tracked.",
			nil, nil,
		),
		ignore: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, "", "nf_conntrack_stat_ignore"),
			"Number of packets seen which are already connected to a conntrack entry.",
			nil, nil,
		),
		insert: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, "", "nf_conntrack_stat_insert"),
			"Number of entries inserted into the list.",
			nil, nil,
		),
		insertFailed: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, "", "nf_conntrack_stat_insert_failed"),
			"Number of entries for which list insertion was attempted but failed.",
			nil, nil,
		),
		drop: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, "", "nf_conntrack_stat_drop"),
			"Number of packets dropped due to conntrack failure.",
			nil, nil,
		),
		earlyDrop: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, "", "nf_conntrack_stat_early_drop"),
			"Number of dropped conntrack entries to make room for new ones, if maximum table size was reached.",
			nil, nil,
		),
		searchRestart: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, "", "nf_conntrack_stat_search_restart"),
			"Number of conntrack table lookups which had to be restarted due to hashtable resizes.",
			nil, nil,
		),
		logger: logger,
	}, nil
}

func (c *conntrackCollector) Update(ch chan<- prometheus.Metric) error {
	value, err := readUintFromFile(procFilePath("sys/net/netfilter/nf_conntrack_count"))
	if err != nil {
		return c.handleErr(err)
	}
	ch <- prometheus.MustNewConstMetric(
		c.current, prometheus.GaugeValue, float64(value))

	value, err = readUintFromFile(procFilePath("sys/net/netfilter/nf_conntrack_max"))
	if err != nil {
		return c.handleErr(err)
	}
	ch <- prometheus.MustNewConstMetric(
		c.limit, prometheus.GaugeValue, float64(value))

	conntrackStats, err := getConntrackStatistics()
	if err != nil {
		return err
	}

	ch <- prometheus.MustNewConstMetric(
		c.found, prometheus.GaugeValue, float64(conntrackStats.found))
	ch <- prometheus.MustNewConstMetric(
		c.invalid, prometheus.GaugeValue, float64(conntrackStats.invalid))
	ch <- prometheus.MustNewConstMetric(
		c.ignore, prometheus.GaugeValue, float64(conntrackStats.ignore))
	ch <- prometheus.MustNewConstMetric(
		c.insert, prometheus.GaugeValue, float64(conntrackStats.insert))
	ch <- prometheus.MustNewConstMetric(
		c.insertFailed, prometheus.GaugeValue, float64(conntrackStats.insertFailed))
	ch <- prometheus.MustNewConstMetric(
		c.drop, prometheus.GaugeValue, float64(conntrackStats.drop))
	ch <- prometheus.MustNewConstMetric(
		c.earlyDrop, prometheus.GaugeValue, float64(conntrackStats.earlyDrop))
	ch <- prometheus.MustNewConstMetric(
		c.searchRestart, prometheus.GaugeValue, float64(conntrackStats.searchRestart))
	return nil
}

func (c *conntrackCollector) handleErr(err error) error {
	if errors.Is(err, os.ErrNotExist) {
		level.Debug(c.logger).Log("msg", "conntrack probably not loaded")
		return ErrNoData
	}
	return fmt.Errorf("failed to retrieve conntrack stats: %w", err)
}

func getConntrackStatistics() (*conntrackStatistics, error) {
	c := conntrackStatistics{}

	fs, err := procfs.NewFS(*procPath)
	if err != nil {
		return nil, fmt.Errorf("failed to open procfs: %w", err)
	}

	connStats, err := fs.ConntrackStat()
	if err != nil {
		return nil, err
	}

	for _, connStat := range connStats {
		c.found += connStat.Found
		c.invalid += connStat.Invalid
		c.ignore += connStat.Ignore
		c.insert += connStat.Insert
		c.insertFailed += connStat.InsertFailed
		c.drop += connStat.Drop
		c.earlyDrop += connStat.EarlyDrop
		c.searchRestart += connStat.SearchRestart
	}

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

// +build !noconntrack

package collector

import (
	"errors"
	"fmt"
	"os"

	"github.com/go-kit/log"
	"github.com/go-kit/log/level"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/procfs"
)

type conntrackCollector struct {
	current       *prometheus.Desc
	limit         *prometheus.Desc
	found         *prometheus.Desc
	invalid       *prometheus.Desc
	ignore        *prometheus.Desc
	insert        *prometheus.Desc
	insertFailed  *prometheus.Desc
	drop          *prometheus.Desc
	earlyDrop     *prometheus.Desc
	searchRestart *prometheus.Desc
	logger        log.Logger
}

type conntrackStatistics struct {
	found         uint64 // Number of searched entries which were successful
	invalid       uint64 // Number of packets seen which can not be tracked
	ignore        uint64 // Number of packets seen which are already connected to a conntrack entry
	insert        uint64 // Number of entries inserted into the list
	insertFailed  uint64 // Number of entries for which list insertion was attempted but failed (happens if the same entry is already present)
	drop          uint64 // Number of packets dropped due to conntrack failure. Either new conntrack entry allocation failed, or protocol helper dropped the packet
	earlyDrop     uint64 // Number of dropped conntrack entries to make room for new ones, if maximum table size was reached
	searchRestart uint64 // Number of conntrack table lookups which had to be restarted due to hashtable resizes
}

func init() {
	registerCollector("conntrack", defaultEnabled, NewConntrackCollector)
}

// NewConntrackCollector returns a new Collector exposing conntrack stats.
func NewConntrackCollector(logger log.Logger) (Collector, error) {
	return &conntrackCollector{
		current: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, "", "nf_conntrack_entries"),
			"Number of currently allocated flow entries for connection tracking.",
			nil, nil,
		),
		limit: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, "", "nf_conntrack_entries_limit"),
			"Maximum size of connection tracking table.",
			nil, nil,
		),
		found: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, "", "nf_conntrack_stat_found"),
			"Number of searched entries which were successful.",
			nil, nil,
		),
		invalid: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, "", "nf_conntrack_stat_invalid"),
			"Number of packets seen which can not be tracked.",
			nil, nil,
		),
		ignore: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, "", "nf_conntrack_stat_ignore"),
			"Number of packets seen which are already connected to a conntrack entry.",
			nil, nil,
		),
		insert: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, "", "nf_conntrack_stat_insert"),
			"Number of entries inserted into the list.",
			nil, nil,
		),
		insertFailed: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, "", "nf_conntrack_stat_insert_failed"),
			"Number of entries for which list insertion was attempted but failed.",
			nil, nil,
		),
		drop: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, "", "nf_conntrack_stat_drop"),
			"Number of packets dropped due to conntrack failure.",
			nil, nil,
		),
		earlyDrop: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, "", "nf_conntrack_stat_early_drop"),
			"Number of dropped conntrack entries to make room for new ones, if maximum table size was reached.",
			nil, nil,
		),
		searchRestart: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, "", "nf_conntrack_stat_search_restart"),
			"Number of conntrack table lookups which had to be restarted due to hashtable resizes.",
			nil, nil,
		),
		logger: logger,
	}, nil
}

func (c *conntrackCollector) Update(ch chan<- prometheus.Metric) error {
	value, err := readUintFromFile(procFilePath("sys/net/netfilter/nf_conntrack_count"))
	if err != nil {
		return c.handleErr(err)
	}
	ch <- prometheus.MustNewConstMetric(
		c.current, prometheus.GaugeValue, float64(value))

	value, err = readUintFromFile(procFilePath("sys/net/netfilter/nf_conntrack_max"))
	if err != nil {
		return c.handleErr(err)
	}
	ch <- prometheus.MustNewConstMetric(
		c.limit, prometheus.GaugeValue, float64(value))

	conntrackStats, err := getConntrackStatistics()
	if err != nil {
		return err
	}

	ch <- prometheus.MustNewConstMetric(
		c.found, prometheus.GaugeValue, float64(conntrackStats.found))
	ch <- prometheus.MustNewConstMetric(
		c.invalid, prometheus.GaugeValue, float64(conntrackStats.invalid))
	ch <- prometheus.MustNewConstMetric(
		c.ignore, prometheus.GaugeValue, float64(conntrackStats.ignore))
	ch <- prometheus.MustNewConstMetric(
		c.insert, prometheus.GaugeValue, float64(conntrackStats.insert))
	ch <- prometheus.MustNewConstMetric(
		c.insertFailed, prometheus.GaugeValue, float64(conntrackStats.insertFailed))
	ch <- prometheus.MustNewConstMetric(
		c.drop, prometheus.GaugeValue, float64(conntrackStats.drop))
	ch <- prometheus.MustNewConstMetric(
		c.earlyDrop, prometheus.GaugeValue, float64(conntrackStats.earlyDrop))
	ch <- prometheus.MustNewConstMetric(
		c.searchRestart, prometheus.GaugeValue, float64(conntrackStats.searchRestart))
	return nil
}

func (c *conntrackCollector) handleErr(err error) error {
	if errors.Is(err, os.ErrNotExist) {
		level.Debug(c.logger).Log("msg", "conntrack probably not loaded")
		return ErrNoData
	}
	return fmt.Errorf("failed to retrieve conntrack stats: %w", err)
}

func getConntrackStatistics() (*conntrackStatistics, error) {
	c := conntrackStatistics{}

	fs, err := procfs.NewFS(*procPath)
	if err != nil {
		return nil, fmt.Errorf("failed to open procfs: %w", err)
	}

	connStats, err := fs.ConntrackStat()
	if err != nil {
		return nil, err
	}

	for _, connStat := range connStats {
		c.found += connStat.Found
		c.invalid += connStat.Invalid
		c.ignore += connStat.Ignore
		c.insert += connStat.Insert
		c.insertFailed += connStat.InsertFailed
		c.drop += connStat.Drop
		c.earlyDrop += connStat.EarlyDrop
		c.searchRestart += connStat.SearchRestart
	}

	return &c, nil
}
<|endoftext|>


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

// +build openbsd
// +build !nomeminfo

package collector

import (
	"fmt"
)

/*
#include <sys/param.h>
#include <sys/types.h>
#include <sys/sysctl.h>

int
sysctl_uvmexp(struct uvmexp *uvmexp)
{
        static int uvmexp_mib[] = {CTL_VM, VM_UVMEXP};
        size_t sz = sizeof(struct uvmexp);

        if(sysctl(uvmexp_mib, 2, uvmexp, &sz, NULL, 0) < 0)
                return -1;

        return 0;
}

*/
import "C"

func (c *meminfoCollector) getMemInfo() (map[string]float64, error) {
	var uvmexp C.struct_uvmexp

	if _, err := C.sysctl_uvmexp(&uvmexp); err != nil {
		return nil, fmt.Errorf("sysctl CTL_VM VM_UVMEXP failed: %v", err)
	}

	ps := float64(uvmexp.pagesize)

	// see uvm(9)
	return map[string]float64{
		"active_bytes":                  ps * float64(uvmexp.active),
		"cache_bytes":                   ps * float64(uvmexp.vnodepages),
		"free_bytes":                    ps * float64(uvmexp.free),
		"inactive_bytes":                ps * float64(uvmexp.inactive),
		"size_bytes":                    ps * float64(uvmexp.npages),
		"swap_size_bytes":               ps * float64(uvmexp.swpages),
		"swap_used_bytes":               ps * float64(uvmexp.swpginuse),
		"swapped_in_pages_bytes_total":  ps * float64(uvmexp.pgswapin),
		"swapped_out_pages_bytes_total": ps * float64(uvmexp.pgswapout),
		"wired_bytes":                   ps * float64(uvmexp.wired),
	}, nil
}
<commit_msg>fix OpenBSD cache memory information (#1542)<commit_after>// Copyright 2015 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build openbsd
// +build !nomeminfo

package collector

import (
	"fmt"
)

/*
#include <sys/param.h>
#include <sys/types.h>
#include <sys/mount.h>
#include <sys/sysctl.h>

int
sysctl_uvmexp(struct uvmexp *uvmexp)
{
        static int uvmexp_mib[] = {CTL_VM, VM_UVMEXP};
        size_t sz = sizeof(struct uvmexp);

        if(sysctl(uvmexp_mib, 2, uvmexp, &sz, NULL, 0) < 0)
                return -1;

        return 0;
}

int
sysctl_bcstats(struct bcachestats *bcstats)
{
        static int bcstats_mib[] = {CTL_VFS, VFS_GENERIC, VFS_BCACHESTAT};
        size_t sz = sizeof(struct bcachestats);

        if(sysctl(bcstats_mib, 3, bcstats, &sz, NULL, 0) < 0)
                return -1;

        return 0;
}

*/
import "C"

func (c *meminfoCollector) getMemInfo() (map[string]float64, error) {
	var uvmexp C.struct_uvmexp
	var bcstats C.struct_bcachestats

	if _, err := C.sysctl_uvmexp(&uvmexp); err != nil {
		return nil, fmt.Errorf("sysctl CTL_VM VM_UVMEXP failed: %v", err)
	}

	if _, err := C.sysctl_bcstats(&bcstats); err != nil {
		return nil, fmt.Errorf("sysctl CTL_VFS VFS_GENERIC VFS_BCACHESTAT failed: %v", err)
	}

	ps := float64(uvmexp.pagesize)

	// see uvm(9)
	return map[string]float64{
		"active_bytes":                  ps * float64(uvmexp.active),
		"cache_bytes":                   ps * float64(bcstats.numbufpages),
		"free_bytes":                    ps * float64(uvmexp.free),
		"inactive_bytes":                ps * float64(uvmexp.inactive),
		"size_bytes":                    ps * float64(uvmexp.npages),
		"swap_size_bytes":               ps * float64(uvmexp.swpages),
		"swap_used_bytes":               ps * float64(uvmexp.swpginuse),
		"swapped_in_pages_bytes_total":  ps * float64(uvmexp.pgswapin),
		"swapped_out_pages_bytes_total": ps * float64(uvmexp.pgswapout),
		"wired_bytes":                   ps * float64(uvmexp.wired),
	}, nil
}
<|endoftext|>


<commit_before>package immortal

import (
	"io/ioutil"
	"os"
	"testing"
	"time"
)

func TestAbsSince(t *testing.T) {
	start := time.Unix(0, 0)
	diff := AbsSince(start)
	if len(diff) < 12 {
		t.Errorf("Check that systems clock are in sync, diff: %s", diff)
	}
}

func TestMd5sumNonexistent(t *testing.T) {
	_, err := md5sum("/dev/null/non-existent")
	if err == nil {
		t.Errorf("Expecting an error")
	}
}

func TestMd5sum(t *testing.T) {
	tmpfile, err := ioutil.TempFile("", "md5")
	if err != nil {
		t.Error(err)
	}
	defer os.Remove(tmpfile.Name())
	content := []byte("The quick brown fox jumps over the lazy dog")
	if _, err := tmpfile.Write(content); err != nil {
		t.Error(err)
	}
	if err := tmpfile.Close(); err != nil {
		t.Error(err)
	}
	md5, err := md5sum(tmpfile.Name())
	if err != nil {
		t.Error(err)
	}
	expect(t, "9e107d9d372bb6826bd81d3542a419d6", md5)
}

func TestInSlice(t *testing.T) {
	var test = []struct {
		slice  []string
		item   string
		expect bool
	}{
		{[]string{"a", "b", "c"}, "x", false},
		{[]string{"a", "b", "c"}, "a", true},
		{[]string{"a a", "b b", "c c"}, "b b", true},
		{[]string{" a", " b", " c"}, "a", false},
		{[]string{" a ", " b ", " c "}, " a ", true},
	}
	for _, tt := range test {
		if i := inSlice(tt.slice, tt.item); i != tt.expect {
			t.Error(tt.slice)
		}
	}
}

func TestIsDir(t *testing.T) {
	expect(t, false, isDir("/dev/null"))
	expect(t, true, isDir("/"))
	expect(t, false, isDir("/etc/hosts"))
}

func TestIsFile(t *testing.T) {
	expect(t, false, isFile("/dev/null"))
	expect(t, false, isFile("/"))
	expect(t, true, isFile("/etc/hosts"))
}
<commit_msg>Test for GetUserSdir<commit_after>package immortal

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"
	"time"
)

func TestAbsSince(t *testing.T) {
	start := time.Unix(0, 0)
	diff := AbsSince(start)
	if len(diff) < 12 {
		t.Errorf("Check that systems clock are in sync, diff: %s", diff)
	}
}

func TestMd5sumNonexistent(t *testing.T) {
	_, err := md5sum("/dev/null/non-existent")
	if err == nil {
		t.Errorf("Expecting an error")
	}
}

func TestMd5sum(t *testing.T) {
	tmpfile, err := ioutil.TempFile("", "md5")
	if err != nil {
		t.Error(err)
	}
	defer os.Remove(tmpfile.Name())
	content := []byte("The quick brown fox jumps over the lazy dog")
	if _, err := tmpfile.Write(content); err != nil {
		t.Error(err)
	}
	if err := tmpfile.Close(); err != nil {
		t.Error(err)
	}
	md5, err := md5sum(tmpfile.Name())
	if err != nil {
		t.Error(err)
	}
	expect(t, "9e107d9d372bb6826bd81d3542a419d6", md5)
}

func TestInSlice(t *testing.T) {
	var test = []struct {
		slice  []string
		item   string
		expect bool
	}{
		{[]string{"a", "b", "c"}, "x", false},
		{[]string{"a", "b", "c"}, "a", true},
		{[]string{"a a", "b b", "c c"}, "b b", true},
		{[]string{" a", " b", " c"}, "a", false},
		{[]string{" a ", " b ", " c "}, " a ", true},
	}
	for _, tt := range test {
		if i := inSlice(tt.slice, tt.item); i != tt.expect {
			t.Error(tt.slice)
		}
	}
}

func TestGetUserSdir(t *testing.T) {
	oldHome := os.Getenv("HOME")

	os.Setenv("HOME", "/tmp/foo")
	if uSdir, err := GetUserSdir(); err != nil {
		t.Error(err)
	} else {
		expect(t, uSdir, filepath.Join("/tmp/foo", ".immortal"))
	}

	os.Setenv("HOME", "")
	if uSdir, err := GetUserSdir(); err != nil {
		t.Error(err)
	} else {
		expect(t, uSdir, filepath.Join(oldHome, ".immortal"))
	}

	os.Setenv("HOME", oldHome)
}

func TestIsDir(t *testing.T) {
	expect(t, false, isDir("/dev/null"))
	expect(t, true, isDir("/"))
	expect(t, false, isDir("/etc/hosts"))
}

func TestIsFile(t *testing.T) {
	expect(t, false, isFile("/dev/null"))
	expect(t, false, isFile("/"))
	expect(t, true, isFile("/etc/hosts"))
}
<|endoftext|>


<commit_before>package command

import (
	"net/url"
	"strings"

	"github.com/mitchellh/cli"
)

// WorkspaceCommand is a Command Implementation that manipulates workspaces,
// which allow multiple distinct states and variables from a single config.
type WorkspaceCommand struct {
	Meta
	LegacyName bool
}

func (c *WorkspaceCommand) Run(args []string) int {
	args, err := c.Meta.process(args, true)
	if err != nil {
		return 1
	}

	envCommandShowWarning(c.Ui, c.LegacyName)

	cmdFlags := c.Meta.extendedFlagSet("workspace")
	cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }

	c.Ui.Output(c.Help())
	return 0
}

func (c *WorkspaceCommand) Help() string {
	helpText := `
Usage: terraform workspace

  New, list, select and delete Terraform workspaces.

`
	return strings.TrimSpace(helpText)
}

func (c *WorkspaceCommand) Synopsis() string {
	return "Workspace management"
}

// validWorkspaceName returns true is this name is valid to use as a workspace name.
// Since most named states are accessed via a filesystem path or URL, check if
// escaping the name would be required.
func validWorkspaceName(name string) bool {
	return name == url.PathEscape(name)
}

func envCommandShowWarning(ui cli.Ui, show bool) {
	if !show {
		return
	}

	ui.Warn(`Warning: the "terraform env" family of commands is deprecated.

"Workspace" is now the preferred term for what earlier Terraform versions
called "environment", to reduce ambiguity caused by the latter term colliding
with other concepts.

The "terraform workspace" commands should be used instead. "terraform env"
will be removed in a future Terraform version.
`)
}

const (
	envNotSupported = `Backend does not support multiple workspaces`

	envExists = `Workspace %q already exists`

	envDoesNotExist = `
Workspace %q doesn't exist.

You can create this workspace with the "new" subcommand.`

	envChanged = `[reset][green]Switched to workspace %q.`

	envCreated = `
[reset][green][bold]Created and switched to workspace %q![reset][green]

You're now on a new, empty workspace. Workspaces isolate their state,
so if you run "terraform plan" Terraform will not see any existing state
for this configuration.
`

	envDeleted = `[reset][green]Deleted workspace %q!`

	envNotEmpty = `
Workspace %[1]q is not empty.

Deleting %[1]q can result in dangling resources: resources that
exist but are no longer manageable by Terraform. Please destroy
these resources first.  If you want to delete this workspace
anyway and risk dangling resources, use the '-force' flag.
`

	envWarnNotEmpty = `[reset][yellow]WARNING: %q was non-empty.
The resources managed by the deleted workspace may still exist,
but are no longer manageable by Terraform since the state has
been deleted.
`

	envDelCurrent = `
Workspace %[1]q is your active workspace.

You cannot delete the currently active workspace. Please switch
to another workspace and try again.
`

	envInvalidName = `
The workspace name %q is not allowed. The name must contain only URL safe
characters, and no path separators.
`

	envIsOverriddenNote = `

The active workspace is being overridden using the TF_WORKSPACE environment
variable.
`

	envIsOverriddenSelectError = `
The selected workspace is currently overridden using the TF_WORKSPACE
environment variable.

To select a new workspace, either update this environment variable or unset
it and then run this command again.
`

	envIsOverriddenNewError = `
The workspace is currently overridden using the TF_WORKSPACE environment
variable. You cannot create a new workspace when using this setting.

To create a new workspace, either unset this environment variable or update it
to match the workspace name you are trying to create, and then run this command
again.
`
)
<commit_msg>command/workspace: Added `show` subcommand to workspace command help text. (#22367)<commit_after>package command

import (
	"net/url"
	"strings"

	"github.com/mitchellh/cli"
)

// WorkspaceCommand is a Command Implementation that manipulates workspaces,
// which allow multiple distinct states and variables from a single config.
type WorkspaceCommand struct {
	Meta
	LegacyName bool
}

func (c *WorkspaceCommand) Run(args []string) int {
	args, err := c.Meta.process(args, true)
	if err != nil {
		return 1
	}

	envCommandShowWarning(c.Ui, c.LegacyName)

	cmdFlags := c.Meta.extendedFlagSet("workspace")
	cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }

	c.Ui.Output(c.Help())
	return 0
}

func (c *WorkspaceCommand) Help() string {
	helpText := `
Usage: terraform workspace

  New, list, show, select and delete Terraform workspaces.

`
	return strings.TrimSpace(helpText)
}

func (c *WorkspaceCommand) Synopsis() string {
	return "Workspace management"
}

// validWorkspaceName returns true is this name is valid to use as a workspace name.
// Since most named states are accessed via a filesystem path or URL, check if
// escaping the name would be required.
func validWorkspaceName(name string) bool {
	return name == url.PathEscape(name)
}

func envCommandShowWarning(ui cli.Ui, show bool) {
	if !show {
		return
	}

	ui.Warn(`Warning: the "terraform env" family of commands is deprecated.

"Workspace" is now the preferred term for what earlier Terraform versions
called "environment", to reduce ambiguity caused by the latter term colliding
with other concepts.

The "terraform workspace" commands should be used instead. "terraform env"
will be removed in a future Terraform version.
`)
}

const (
	envNotSupported = `Backend does not support multiple workspaces`

	envExists = `Workspace %q already exists`

	envDoesNotExist = `
Workspace %q doesn't exist.

You can create this workspace with the "new" subcommand.`

	envChanged = `[reset][green]Switched to workspace %q.`

	envCreated = `
[reset][green][bold]Created and switched to workspace %q![reset][green]

You're now on a new, empty workspace. Workspaces isolate their state,
so if you run "terraform plan" Terraform will not see any existing state
for this configuration.
`

	envDeleted = `[reset][green]Deleted workspace %q!`

	envNotEmpty = `
Workspace %[1]q is not empty.

Deleting %[1]q can result in dangling resources: resources that
exist but are no longer manageable by Terraform. Please destroy
these resources first.  If you want to delete this workspace
anyway and risk dangling resources, use the '-force' flag.
`

	envWarnNotEmpty = `[reset][yellow]WARNING: %q was non-empty.
The resources managed by the deleted workspace may still exist,
but are no longer manageable by Terraform since the state has
been deleted.
`

	envDelCurrent = `
Workspace %[1]q is your active workspace.

You cannot delete the currently active workspace. Please switch
to another workspace and try again.
`

	envInvalidName = `
The workspace name %q is not allowed. The name must contain only URL safe
characters, and no path separators.
`

	envIsOverriddenNote = `

The active workspace is being overridden using the TF_WORKSPACE environment
variable.
`

	envIsOverriddenSelectError = `
The selected workspace is currently overridden using the TF_WORKSPACE
environment variable.

To select a new workspace, either update this environment variable or unset
it and then run this command again.
`

	envIsOverriddenNewError = `
The workspace is currently overridden using the TF_WORKSPACE environment
variable. You cannot create a new workspace when using this setting.

To create a new workspace, either unset this environment variable or update it
to match the workspace name you are trying to create, and then run this command
again.
`
)
<|endoftext|>


<commit_before>package storage

import (
	as "github.com/go-ap/activitystreams"
)

// Loader
type Loader interface {
	Load(f Filterable) (as.ItemCollection, int, error)
}

// ActivityLoader
type ActivityLoader interface {
	LoadActivities(f Filterable) (as.ItemCollection, int, error)
}

// ActorLoader
type ActorLoader interface {
	LoadActors(f Filterable) (as.ItemCollection, int, error)
}

// ObjectLoader
type ObjectLoader interface {
	LoadObjects(f Filterable) (as.ItemCollection, int, error)
}

// ActivitySaver
type ActivitySaver interface {
	SaveActivity(as.Item) (as.Item, error)
}

// ActorSaver
type ActorSaver interface {
	SaveActor(as.Item) (as.Item, error)
}

// ObjectSaver
type ObjectSaver interface {
	SaveObject(as.Item) (as.Item, error)
}
<commit_msg>Added CollectionLoader interface<commit_after>package storage

import (
	as "github.com/go-ap/activitystreams"
)

// Loader
type Loader interface {
	Load(f Filterable) (as.ItemCollection, int, error)
}

// ActivityLoader
type ActivityLoader interface {
	LoadActivities(f Filterable) (as.ItemCollection, int, error)
}

// ActorLoader
type ActorLoader interface {
	LoadActors(f Filterable) (as.ItemCollection, int, error)
}

// ObjectLoader
type ObjectLoader interface {
	LoadObjects(f Filterable) (as.ItemCollection, int, error)
}

// CollectionLoader
type CollectionLoader interface {
	LoadCollection(f Filterable) (as.CollectionInterface, int, error)
}

// ActivitySaver
type ActivitySaver interface {
	SaveActivity(as.Item) (as.Item, error)
}

// ActorSaver
type ActorSaver interface {
	SaveActor(as.Item) (as.Item, error)
}

// ObjectSaver
type ObjectSaver interface {
	SaveObject(as.Item) (as.Item, error)
}
<|endoftext|>


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

package monitor

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"

	"github.com/golang/protobuf/jsonpb"
	"golang.org/x/net/context"

	"go.chromium.org/luci/common/clock"
	"go.chromium.org/luci/common/lhttp"
	"go.chromium.org/luci/common/logging"
	pb "go.chromium.org/luci/common/tsmon/ts_mon_proto"
	"go.chromium.org/luci/common/tsmon/types"
)

var (
	// ProdxmonScopes is the list of oauth2 scopes needed on the http client
	// given to NewHTTPMonitor.
	ProdxmonScopes = []string{"https://www.googleapis.com/auth/prodxmon"}
)

type httpMonitor struct {
	client   *http.Client
	endpoint *url.URL
}

// NewHTTPMonitor creates a new Monitor object that sends metric to an HTTP
// (or HTTPS) endpoint.  The http client should be authenticated as required.
func NewHTTPMonitor(ctx context.Context, client *http.Client, endpoint *url.URL) (Monitor, error) {
	return &httpMonitor{
		client:   client,
		endpoint: endpoint,
	}, nil
}

func (m *httpMonitor) ChunkSize() int {
	return 500
}

func (m *httpMonitor) Send(ctx context.Context, cells []types.Cell) error {
	// Serialize the tsmon cells into protobufs.
	req := &pb.Request{
		Payload: &pb.MetricsPayload{
			MetricsCollection: SerializeCells(cells, clock.Now(ctx)),
		},
	}

	// JSON encode the request.
	encoded := bytes.Buffer{}
	marshaller := jsonpb.Marshaler{}
	if err := marshaller.Marshal(&encoded, req); err != nil {
		return err
	}

	// Make the request.
	status, err := lhttp.NewRequest(ctx, m.client, nil, func() (*http.Request, error) {
		req, err := http.NewRequest("POST", m.endpoint.String(), bytes.NewReader(encoded.Bytes()))
		if err != nil {
			return nil, err
		}
		req.Header.Set("Content-Type", "application/json")
		return req, nil
	}, func(resp *http.Response) error {
		return resp.Body.Close()
	}, func(resp *http.Response, oErr error) error {
		if resp != nil {
			body, err := ioutil.ReadAll(resp.Body)
			if err != nil {
				logging.WithError(err).Errorf(ctx, "Failed to read error response body")
			} else {
				logging.Errorf(ctx, "Monitoring push failed.  Response body: %s", body)
			}
			resp.Body.Close()
		}
		// On HTTP 429 response (Too many requests) oErr is marked as transient and
		// returning it causes a retry. We don't want to do that. HTTP 429 is
		// received if timestamps in the request body indicate that the sampling
		// period is smaller than the configured retention period. Resending the
		// exact same body with exact same timestamps won't help. Return a fatal
		// error instead.
		if resp != nil && resp.StatusCode == 429 {
			return fmt.Errorf("giving up on HTTP 249 status")
		}
		return oErr
	})()
	if err != nil {
		return err
	}
	if status != http.StatusOK {
		return fmt.Errorf("bad response status %d from endpoint %s", status, m.endpoint)
	}

	logging.Debugf(ctx, "Sent %d tsmon cells to %s", len(cells), m.endpoint)
	return nil
}

func (m *httpMonitor) Close() error {
	return nil
}
<commit_msg>[tsmon] Log bodies of requests that are rejected by tsmon backend.<commit_after>// Copyright 2016 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package monitor

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"

	"github.com/golang/protobuf/jsonpb"
	"golang.org/x/net/context"

	"go.chromium.org/luci/common/clock"
	"go.chromium.org/luci/common/lhttp"
	"go.chromium.org/luci/common/logging"
	pb "go.chromium.org/luci/common/tsmon/ts_mon_proto"
	"go.chromium.org/luci/common/tsmon/types"
)

var (
	// ProdxmonScopes is the list of oauth2 scopes needed on the http client
	// given to NewHTTPMonitor.
	ProdxmonScopes = []string{"https://www.googleapis.com/auth/prodxmon"}
)

type httpMonitor struct {
	client   *http.Client
	endpoint *url.URL
}

// NewHTTPMonitor creates a new Monitor object that sends metric to an HTTP
// (or HTTPS) endpoint.  The http client should be authenticated as required.
func NewHTTPMonitor(ctx context.Context, client *http.Client, endpoint *url.URL) (Monitor, error) {
	return &httpMonitor{
		client:   client,
		endpoint: endpoint,
	}, nil
}

func (m *httpMonitor) ChunkSize() int {
	return 500
}

func (m *httpMonitor) Send(ctx context.Context, cells []types.Cell) error {
	// Serialize the tsmon cells into protobufs.
	req := &pb.Request{
		Payload: &pb.MetricsPayload{
			MetricsCollection: SerializeCells(cells, clock.Now(ctx)),
		},
	}

	// JSON encode the request.
	encoded := bytes.Buffer{}
	marshaller := jsonpb.Marshaler{}
	if err := marshaller.Marshal(&encoded, req); err != nil {
		return err
	}

	// Make the request.
	status, err := lhttp.NewRequest(ctx, m.client, nil, func() (*http.Request, error) {
		req, err := http.NewRequest("POST", m.endpoint.String(), bytes.NewReader(encoded.Bytes()))
		if err != nil {
			return nil, err
		}
		req.Header.Set("Content-Type", "application/json")
		return req, nil
	}, func(resp *http.Response) error {
		return resp.Body.Close()
	}, func(resp *http.Response, oErr error) error {
		if resp != nil {
			body, err := ioutil.ReadAll(resp.Body)
			if err != nil {
				logging.WithError(err).Errorf(ctx, "Failed to read error response body")
			} else {
				logging.Errorf(
					ctx, "Monitoring push failed.\nResponse body: %s\nRequest body: %s",
					body, encoded.Bytes())
			}
			resp.Body.Close()
		}
		// On HTTP 429 response (Too many requests) oErr is marked as transient and
		// returning it causes a retry. We don't want to do that. HTTP 429 is
		// received if timestamps in the request body indicate that the sampling
		// period is smaller than the configured retention period. Resending the
		// exact same body with exact same timestamps won't help. Return a fatal
		// error instead.
		if resp != nil && resp.StatusCode == 429 {
			return fmt.Errorf("giving up on HTTP 249 status")
		}
		return oErr
	})()
	if err != nil {
		return err
	}
	if status != http.StatusOK {
		return fmt.Errorf("bad response status %d from endpoint %s", status, m.endpoint)
	}

	logging.Debugf(ctx, "Sent %d tsmon cells to %s", len(cells), m.endpoint)
	return nil
}

func (m *httpMonitor) Close() error {
	return nil
}
<|endoftext|>


<commit_before>// +build go1.8

package proxy

import (
	"bytes"
	"context"
	"database/sql/driver"
	"fmt"
	"io"
	"sync"
	"time"
)

// TracerOptions holds the tarcing option.
type TracerOptions struct {
	// Outputter is the output of the log.
	Outputter Outputter

	// Filter is used by the tracing proxy for skipping database libraries (e.g. O/R mapper).
	Filter Filter

	// SlowQuery is a threshold duration to output into log.
	// output all queries if SlowQuery is zero.
	SlowQuery time.Duration
}

// NewTraceProxy generates a proxy that logs queries.
func NewTraceProxy(d driver.Driver, o Outputter) *Proxy {
	return NewProxyContext(d, NewTraceHooks(TracerOptions{
		Outputter: o,
	}))
}

// NewTraceProxyWithFilter generates a proxy that logs queries.
func NewTraceProxyWithFilter(d driver.Driver, o Outputter, f Filter) *Proxy {
	return NewProxyContext(d, NewTraceHooks(TracerOptions{
		Outputter: o,
		Filter:    f,
	}))
}

func NewTraceHooks(opt TracerOptions) *HooksContext {
	f := opt.Filter
	if f == nil {
		f = DefaultPackageFilter
	}
	o := opt.Outputter
	if o == nil {
		o = logger{}
	}
	pool := &sync.Pool{
		New: func() interface{} {
			return &bytes.Buffer{}
		},
	}
	return &HooksContext{
		PreOpen: func(_ context.Context, _ string) (interface{}, error) {
			return time.Now(), nil
		},
		PostOpen: func(_ context.Context, ctx interface{}, _ driver.Conn, err error) error {
			d := time.Since(ctx.(time.Time))
			if d < opt.SlowQuery {
				return nil
			}
			buf := pool.Get().(*bytes.Buffer)
			buf.Reset()
			io.WriteString(buf, "Open")
			if err != nil {
				fmt.Fprintf(buf, "; err = %#v", err.Error())
			}
			io.WriteString(buf, " (")
			io.WriteString(buf, d.String())
			io.WriteString(buf, ")")
			o.Output(findCaller(f), buf.String())
			pool.Put(buf)
			return nil
		},
		PreExec: func(_ context.Context, _ *Stmt, _ []driver.NamedValue) (interface{}, error) {
			return time.Now(), nil
		},
		PostExec: func(_ context.Context, ctx interface{}, stmt *Stmt, args []driver.NamedValue, _ driver.Result, err error) error {
			d := time.Since(ctx.(time.Time))
			if d < opt.SlowQuery {
				return nil
			}
			buf := pool.Get().(*bytes.Buffer)
			buf.Reset()
			io.WriteString(buf, "Exec: ")
			io.WriteString(buf, stmt.QueryString)
			io.WriteString(buf, "; args = [")
			writeNamedValues(buf, args)
			io.WriteString(buf, "]")
			if err != nil {
				fmt.Fprintf(buf, "; err = %#v", err.Error())
			}
			io.WriteString(buf, " (")
			io.WriteString(buf, d.String())
			io.WriteString(buf, ")")
			o.Output(findCaller(f), buf.String())
			pool.Put(buf)
			return nil
		},
		PreQuery: func(_ context.Context, stmt *Stmt, args []driver.NamedValue) (interface{}, error) {
			return time.Now(), nil
		},
		PostQuery: func(_ context.Context, ctx interface{}, stmt *Stmt, args []driver.NamedValue, _ driver.Rows, err error) error {
			d := time.Since(ctx.(time.Time))
			if d < opt.SlowQuery {
				return nil
			}
			buf := pool.Get().(*bytes.Buffer)
			buf.Reset()
			io.WriteString(buf, "Query: ")
			io.WriteString(buf, stmt.QueryString)
			io.WriteString(buf, "; args = [")
			writeNamedValues(buf, args)
			io.WriteString(buf, "]")
			if err != nil {
				fmt.Fprintf(buf, "; err = %#v", err.Error())
			}
			io.WriteString(buf, " (")
			io.WriteString(buf, d.String())
			io.WriteString(buf, ")")
			o.Output(findCaller(f), buf.String())
			pool.Put(buf)
			return nil
		},
		PreBegin: func(_ context.Context, _ *Conn) (interface{}, error) {
			return time.Now(), nil
		},
		PostBegin: func(_ context.Context, ctx interface{}, _ *Conn, err error) error {
			d := time.Since(ctx.(time.Time))
			if d < opt.SlowQuery {
				return nil
			}
			buf := pool.Get().(*bytes.Buffer)
			buf.Reset()
			io.WriteString(buf, "Begin")
			if err != nil {
				fmt.Fprintf(buf, "; err = %#v", err.Error())
			}
			io.WriteString(buf, " (")
			io.WriteString(buf, d.String())
			io.WriteString(buf, ")")
			o.Output(findCaller(f), buf.String())
			pool.Put(buf)
			return nil
		},
		PreCommit: func(_ context.Context, _ *Tx) (interface{}, error) {
			return time.Now(), nil
		},
		PostCommit: func(_ context.Context, ctx interface{}, _ *Tx, err error) error {
			d := time.Since(ctx.(time.Time))
			if d < opt.SlowQuery {
				return nil
			}
			buf := pool.Get().(*bytes.Buffer)
			buf.Reset()
			io.WriteString(buf, "Commit")
			if err != nil {
				fmt.Fprintf(buf, "; err = %#v", err.Error())
			}
			io.WriteString(buf, " (")
			io.WriteString(buf, d.String())
			io.WriteString(buf, ")")
			o.Output(findCaller(f), buf.String())
			pool.Put(buf)
			return nil
		},
		PreRollback: func(_ context.Context, _ *Tx) (interface{}, error) {
			return time.Now(), nil
		},
		PostRollback: func(_ context.Context, ctx interface{}, _ *Tx, err error) error {
			d := time.Since(ctx.(time.Time))
			if d < opt.SlowQuery {
				return nil
			}
			buf := pool.Get().(*bytes.Buffer)
			buf.Reset()
			io.WriteString(buf, "Rollback")
			if err != nil {
				fmt.Fprintf(buf, "; err = %#v", err.Error())
			}
			io.WriteString(buf, " (")
			io.WriteString(buf, d.String())
			io.WriteString(buf, ")")
			o.Output(findCaller(f), buf.String())
			pool.Put(buf)
			return nil
		},
	}
}

func writeNamedValues(w io.Writer, args []driver.NamedValue) {
	for i, arg := range args {
		if i != 0 {
			io.WriteString(w, ", ")
		}
		if len(arg.Name) > 0 {
			io.WriteString(w, arg.Name)
			io.WriteString(w, ":")
		}
		fmt.Fprintf(w, "%#v", arg.Value)
	}
}
<commit_msg>return buffers to pool earlier<commit_after>// +build go1.8

package proxy

import (
	"bytes"
	"context"
	"database/sql/driver"
	"fmt"
	"io"
	"sync"
	"time"
)

// TracerOptions holds the tarcing option.
type TracerOptions struct {
	// Outputter is the output of the log.
	Outputter Outputter

	// Filter is used by the tracing proxy for skipping database libraries (e.g. O/R mapper).
	Filter Filter

	// SlowQuery is a threshold duration to output into log.
	// output all queries if SlowQuery is zero.
	SlowQuery time.Duration
}

// NewTraceProxy generates a proxy that logs queries.
func NewTraceProxy(d driver.Driver, o Outputter) *Proxy {
	return NewProxyContext(d, NewTraceHooks(TracerOptions{
		Outputter: o,
	}))
}

// NewTraceProxyWithFilter generates a proxy that logs queries.
func NewTraceProxyWithFilter(d driver.Driver, o Outputter, f Filter) *Proxy {
	return NewProxyContext(d, NewTraceHooks(TracerOptions{
		Outputter: o,
		Filter:    f,
	}))
}

func NewTraceHooks(opt TracerOptions) *HooksContext {
	f := opt.Filter
	if f == nil {
		f = DefaultPackageFilter
	}
	o := opt.Outputter
	if o == nil {
		o = logger{}
	}
	pool := &sync.Pool{
		New: func() interface{} {
			return &bytes.Buffer{}
		},
	}
	return &HooksContext{
		PreOpen: func(_ context.Context, _ string) (interface{}, error) {
			return time.Now(), nil
		},
		PostOpen: func(_ context.Context, ctx interface{}, _ driver.Conn, err error) error {
			d := time.Since(ctx.(time.Time))
			if d < opt.SlowQuery {
				return nil
			}
			buf := pool.Get().(*bytes.Buffer)
			buf.Reset()
			io.WriteString(buf, "Open")
			if err != nil {
				fmt.Fprintf(buf, "; err = %#v", err.Error())
			}
			io.WriteString(buf, " (")
			io.WriteString(buf, d.String())
			io.WriteString(buf, ")")
			s := buf.String()
			pool.Put(buf)
			o.Output(findCaller(f), s)
			return nil
		},
		PreExec: func(_ context.Context, _ *Stmt, _ []driver.NamedValue) (interface{}, error) {
			return time.Now(), nil
		},
		PostExec: func(_ context.Context, ctx interface{}, stmt *Stmt, args []driver.NamedValue, _ driver.Result, err error) error {
			d := time.Since(ctx.(time.Time))
			if d < opt.SlowQuery {
				return nil
			}
			buf := pool.Get().(*bytes.Buffer)
			buf.Reset()
			io.WriteString(buf, "Exec: ")
			io.WriteString(buf, stmt.QueryString)
			io.WriteString(buf, "; args = [")
			writeNamedValues(buf, args)
			io.WriteString(buf, "]")
			if err != nil {
				fmt.Fprintf(buf, "; err = %#v", err.Error())
			}
			io.WriteString(buf, " (")
			io.WriteString(buf, d.String())
			io.WriteString(buf, ")")
			s := buf.String()
			pool.Put(buf)
			o.Output(findCaller(f), s)
			return nil
		},
		PreQuery: func(_ context.Context, stmt *Stmt, args []driver.NamedValue) (interface{}, error) {
			return time.Now(), nil
		},
		PostQuery: func(_ context.Context, ctx interface{}, stmt *Stmt, args []driver.NamedValue, _ driver.Rows, err error) error {
			d := time.Since(ctx.(time.Time))
			if d < opt.SlowQuery {
				return nil
			}
			buf := pool.Get().(*bytes.Buffer)
			buf.Reset()
			io.WriteString(buf, "Query: ")
			io.WriteString(buf, stmt.QueryString)
			io.WriteString(buf, "; args = [")
			writeNamedValues(buf, args)
			io.WriteString(buf, "]")
			if err != nil {
				fmt.Fprintf(buf, "; err = %#v", err.Error())
			}
			io.WriteString(buf, " (")
			io.WriteString(buf, d.String())
			io.WriteString(buf, ")")
			s := buf.String()
			pool.Put(buf)
			o.Output(findCaller(f), s)
			return nil
		},
		PreBegin: func(_ context.Context, _ *Conn) (interface{}, error) {
			return time.Now(), nil
		},
		PostBegin: func(_ context.Context, ctx interface{}, _ *Conn, err error) error {
			d := time.Since(ctx.(time.Time))
			if d < opt.SlowQuery {
				return nil
			}
			buf := pool.Get().(*bytes.Buffer)
			buf.Reset()
			io.WriteString(buf, "Begin")
			if err != nil {
				fmt.Fprintf(buf, "; err = %#v", err.Error())
			}
			io.WriteString(buf, " (")
			io.WriteString(buf, d.String())
			io.WriteString(buf, ")")
			s := buf.String()
			pool.Put(buf)
			o.Output(findCaller(f), s)
			return nil
		},
		PreCommit: func(_ context.Context, _ *Tx) (interface{}, error) {
			return time.Now(), nil
		},
		PostCommit: func(_ context.Context, ctx interface{}, _ *Tx, err error) error {
			d := time.Since(ctx.(time.Time))
			if d < opt.SlowQuery {
				return nil
			}
			buf := pool.Get().(*bytes.Buffer)
			buf.Reset()
			io.WriteString(buf, "Commit")
			if err != nil {
				fmt.Fprintf(buf, "; err = %#v", err.Error())
			}
			io.WriteString(buf, " (")
			io.WriteString(buf, d.String())
			io.WriteString(buf, ")")
			s := buf.String()
			pool.Put(buf)
			o.Output(findCaller(f), s)
			return nil
		},
		PreRollback: func(_ context.Context, _ *Tx) (interface{}, error) {
			return time.Now(), nil
		},
		PostRollback: func(_ context.Context, ctx interface{}, _ *Tx, err error) error {
			d := time.Since(ctx.(time.Time))
			if d < opt.SlowQuery {
				return nil
			}
			buf := pool.Get().(*bytes.Buffer)
			buf.Reset()
			io.WriteString(buf, "Rollback")
			if err != nil {
				fmt.Fprintf(buf, "; err = %#v", err.Error())
			}
			io.WriteString(buf, " (")
			io.WriteString(buf, d.String())
			io.WriteString(buf, ")")
			s := buf.String()
			pool.Put(buf)
			o.Output(findCaller(f), s)
			return nil
		},
	}
}

func writeNamedValues(w io.Writer, args []driver.NamedValue) {
	for i, arg := range args {
		if i != 0 {
			io.WriteString(w, ", ")
		}
		if len(arg.Name) > 0 {
			io.WriteString(w, arg.Name)
			io.WriteString(w, ":")
		}
		fmt.Fprintf(w, "%#v", arg.Value)
	}
}
<|endoftext|>


<commit_before>package gdal

/*
#include "go_gdal.h"
#include "gdal_version.h"
#include "gdal_utils.h"

#cgo linux  pkg-config: gdal
#cgo darwin pkg-config: gdal
#cgo windows LDFLAGS: -Lc:/gdal/release-1600-x64/lib -lgdal_i
#cgo windows CFLAGS: -IC:/gdal/release-1600-x64/include
*/
import "C"
import (
	"fmt"
	"unsafe"
)

var _ = fmt.Println

/* --------------------------------------------- */
/* GDAL utilities                                */
/* --------------------------------------------- */

type GDALTranslateOptions struct {
	cval *C.GDALTranslateOptions
}

func GDALTranslate(
	destName string,
	srcDS Dataset,
	options []string,
) Dataset {

	var err C.int

	length := len(options)
	cOptions := make([]*C.char, length+1)
	for i := 0; i < length; i++ {
		cOptions[i] = C.CString(options[i])
		defer C.free(unsafe.Pointer(cOptions[i]))
	}
	cOptions[length] = (*C.char)(unsafe.Pointer(nil))

	gdalTranslateOptions := GDALTranslateOptions{C.GDALTranslateOptionsNew((**C.char)(unsafe.Pointer(&cOptions[0])), nil)}

	outputDs := C.GDALTranslate(
		C.CString(destName),
		srcDS.cval,
		gdalTranslateOptions.cval,
		&err,
	)

	return Dataset{outputDs}

}
<commit_msg>amend previous commit<commit_after>package gdal

/*
#include "go_gdal.h"
#include "gdal_version.h"
#include "gdal_utils.h"

#cgo linux  pkg-config: gdal
#cgo darwin pkg-config: gdal
#cgo windows LDFLAGS: -Lc:/gdal/release-1600-x64/lib -lgdal_i
#cgo windows CFLAGS: -IC:/gdal/release-1600-x64/include
*/
import "C"
import (
	"fmt"
	"unsafe"
)

var _ = fmt.Println

/* --------------------------------------------- */
/* GDAL utilities                                */
/* --------------------------------------------- */

type GDALTranslateOptions struct {
	cval *C.GDALTranslateOptions
}
type GDALWarpAppOptions struct {
	cval *C.GDALWarpAppOptions
}

func GDALTranslate(
	destName string,
	srcDS Dataset,
	options []string,
) Dataset {

	var err C.int

	length := len(options)
	cOptions := make([]*C.char, length+1)
	for i := 0; i < length; i++ {
		cOptions[i] = C.CString(options[i])
		defer C.free(unsafe.Pointer(cOptions[i]))
	}
	cOptions[length] = (*C.char)(unsafe.Pointer(nil))

	gdalTranslateOptions := GDALTranslateOptions{C.GDALTranslateOptionsNew((**C.char)(unsafe.Pointer(&cOptions[0])), nil)}

	outputDs := C.GDALTranslate(
		C.CString(destName),
		srcDS.cval,
		gdalTranslateOptions.cval,
		&err,
	)

	return Dataset{outputDs}

}

// func GDALWarpCreateOutput(
// 	nSrcCount int,
// 	srcDS []Dataset,
// 	pszFilename string,
// 	pszFormat string,
// 	papszTO []string,
// 	ppapszCreateOptions *[]string,
// 	eDT DataType,
// 	phTransformArg unsafe.Pointer,
// 	bSetColorInterpretation bool,
// 	psOptions GDALWarpAppOptions,

// ) GDALWarpAppOptions {

// 	pahSrcDs := make([]C.GDALDatasetH, len(srcDs)+1)
// 	for i := 0; i < len(srcDs); i++ {
// 		pahSrcDs[i] = srcDs[i].cval
// 	}
// 	pahSrcDs[len(srcDs)] = (C.GDALDatasetH)(unsafe.Pointer(nil))

// 	hDstDS := C.GDALWarpCreateOutput(
// 		C.int(nSrcCount),
// 	)
// }

func GDALWarp(
	destName string,
	dstDs Dataset,
	srcDs []Dataset,
	options []string,
) Dataset {

	var err C.int

	length := len(options)
	cOptions := make([]*C.char, length+1)
	for i := 0; i < length; i++ {
		cOptions[i] = C.CString(options[i])
		defer C.free(unsafe.Pointer(cOptions[i]))
	}
	cOptions[length] = (*C.char)(unsafe.Pointer(nil))

	gdalWarpOptions := GDALWarpAppOptions{C.GDALWarpAppOptionsNew((**C.char)(unsafe.Pointer(&cOptions[0])), nil)}

	pahSrcDs := make([]C.GDALDatasetH, len(srcDs)+1)
	for i := 0; i < len(srcDs); i++ {
		pahSrcDs[i] = srcDs[i].cval
		// defer C.free(unsafe.Pointer(pahSrcDs[i]))
	}
	pahSrcDs[len(srcDs)] = (C.GDALDatasetH)(unsafe.Pointer(nil))

	outputDs := C.GDALWarp(
		C.CString(destName),
		dstDs.cval,
		C.int(len(srcDs)),
		(*C.GDALDatasetH)(unsafe.Pointer(&pahSrcDs[0])),
		gdalWarpOptions.cval,
		&err,
	)

	return Dataset{outputDs}

}
<|endoftext|>


<commit_before>package lib

import (
	"bytes"
	"context"
	"fmt"
	"regexp"
	"sort"
	"strings"
	"text/template"

	"github.com/google/go-github/github"
)

// Format is Formatter
type Format struct {
	ctx    context.Context
	client *github.Client
	debug  bool
}

// NewFormat is an initializer
func NewFormat(ctx context.Context, client *github.Client, debug bool) *Format {
	return &Format{ctx: ctx, client: client, debug: debug}
}

// Line is line infomation
type Line struct {
	title    string
	repoName string
	url      string
	user     string
	status   string
}

// Line returns Issue/PR info retrieving from GitHub
func (f *Format) Line(event *github.Event, i int) Line {
	if f.debug {
		fmt.Printf("%2d %s\n", i, htmlURL(event))
	}

	payload := event.Payload()
	var line Line

	switch *event.Type {
	case "IssuesEvent":
		e := payload.(*github.IssuesEvent)
		issue := getIssue(f.ctx, f.client, *event.Repo.Name, *e.Issue.Number)

		if issue != nil {
			if issue.PullRequestLinks == nil {
				line = Line{
					title:    *issue.Title,
					repoName: *event.Repo.Name,
					url:      *issue.HTMLURL,
					user:     *issue.User.Login,
					status:   getIssueStatus(issue),
				}
			} else {
				pr := getPullRequest(f.ctx, f.client, *event.Repo.Name, *e.Issue.Number)

				line = Line{
					title:    *pr.Title,
					repoName: *event.Repo.Name,
					url:      *pr.HTMLURL,
					user:     *pr.User.Login,
					status:   getPullRequestStatus(pr),
				}
			}
		} else {
			line = Line{
				title:    *e.Issue.Title,
				repoName: *event.Repo.Name,
				url:      *e.Issue.HTMLURL,
				user:     *e.Issue.User.Login,
				status:   getIssueStatus(e.Issue),
			}
		}
	case "IssueCommentEvent":
		e := payload.(*github.IssueCommentEvent)
		issue := getIssue(f.ctx, f.client, *event.Repo.Name, *e.Issue.Number)

		if issue != nil {
			if issue.PullRequestLinks == nil {
				line = Line{
					title:    *issue.Title,
					repoName: *event.Repo.Name,
					url:      *issue.HTMLURL,
					user:     *issue.User.Login,
					status:   getIssueStatus(issue),
				}
			} else {
				pr := getPullRequest(f.ctx, f.client, *event.Repo.Name, *e.Issue.Number)

				line = Line{
					title:    *pr.Title,
					repoName: *event.Repo.Name,
					url:      *pr.HTMLURL,
					user:     *pr.User.Login,
					status:   getPullRequestStatus(pr),
				}
			}
		} else {
			line = Line{
				title:    *e.Issue.Title,
				repoName: *event.Repo.Name,
				url:      *e.Issue.HTMLURL,
				user:     *e.Issue.User.Login,
				status:   getIssueStatus(e.Issue),
			}
		}
	case "PullRequestEvent":
		e := payload.(*github.PullRequestEvent)
		pr := getPullRequest(f.ctx, f.client, *event.Repo.Name, e.GetNumber())
		if pr != nil {
			line = Line{
				title:    *pr.Title,
				repoName: *event.Repo.Name,
				url:      *pr.HTMLURL,
				user:     *pr.User.Login,
				status:   getPullRequestStatus(pr),
			}
		} else {
			line = Line{
				title:    *e.PullRequest.Title,
				repoName: *event.Repo.Name,
				url:      *e.PullRequest.HTMLURL,
				user:     *e.PullRequest.User.Login,
				status:   getPullRequestStatus(e.PullRequest),
			}
		}
	case "PullRequestReviewCommentEvent":
		e := payload.(*github.PullRequestReviewCommentEvent)
		pr := getPullRequest(f.ctx, f.client, *event.Repo.Name, *e.PullRequest.Number)
		if pr != nil {
			line = Line{
				title:    *pr.Title,
				repoName: *event.Repo.Name,
				url:      *pr.HTMLURL,
				user:     *pr.User.Login,
				status:   getPullRequestStatus(pr),
			}
		} else {
			line = Line{
				title:    *e.PullRequest.Title,
				repoName: *event.Repo.Name,
				url:      *e.PullRequest.HTMLURL,
				user:     *e.PullRequest.User.Login,
				status:   getPullRequestStatus(e.PullRequest),
			}
		}
	}

	return line
}

func getIssue(ctx context.Context, client *github.Client, repoFullName string, number int) *github.Issue {
	owner, repo := getOwnerRepo(repoFullName)
	issue, _, _ := client.Issues.Get(ctx, owner, repo, number)
	return issue
}

func getPullRequest(ctx context.Context, client *github.Client, repoFullName string, number int) *github.PullRequest {
	owner, repo := getOwnerRepo(repoFullName)
	pr, _, _ := client.PullRequests.Get(ctx, owner, repo, number)
	return pr
}

func getIssueStatus(issue *github.Issue) string {
	result := ""
	if *issue.State == "closed" {
		result = "closed"
	}
	return result
}

func getPullRequestStatus(pr *github.PullRequest) string {
	result := ""
	if *pr.Merged {
		result = "merged"
	} else if *pr.State == "closed" {
		result = "closed"
	}
	return result
}

func getOwnerRepo(repoFullName string) (string, string) {
	s := strings.Split(repoFullName, "/")
	owner := s[0]
	repo := s[1]
	return owner, repo
}

// All returns all lines which are formatted and sorted
func (f *Format) All(lines Lines) (string, error) {
	var result, prevRepoName, currentRepoName string
	var settings Settings

	if err := settings.Init(); err != nil {
		return "", err
	}

	sort.Sort(lines)

	for _, line := range lines {
		currentRepoName = line.repoName

		if currentRepoName != prevRepoName {
			prevRepoName = currentRepoName
			result += fmt.Sprintf("\n%s\n\n", formatSubject(settings, currentRepoName))
		}

		result += fmt.Sprintf("%s\n", formatLine(settings, line))
	}

	return result, nil
}

// Lines has sort.Interface
type Lines []Line

func (l Lines) Len() int {
	return len(l)
}

func (l Lines) Swap(i, j int) {
	l[i], l[j] = l[j], l[i]
}

func (l Lines) Less(i, j int) bool {
	return l[i].url < l[j].url
}

func formatSubject(settings Settings, repoName string) string {
	formatSubject := convertNamedParameters(settings.Format.Subject)

	m := map[string]interface{}{"subject": repoName}
	t := template.Must(template.New("").Parse(formatSubject))

	var rendered bytes.Buffer
	t.Execute(&rendered, m)

	return rendered.String()
}

func formatLine(settings Settings, line Line) string {
	formatLine := convertNamedParameters(settings.Format.Line)

	m := map[string]interface{}{
		"title":  line.title,
		"url":    line.url,
		"user":   line.user,
		"status": formatStatus(settings, line.status),
	}
	t := template.Must(template.New("").Parse(formatLine))

	var rendered bytes.Buffer
	t.Execute(&rendered, m)

	return strings.TrimSpace(rendered.String())
}

func formatStatus(settings Settings, status string) string {
	switch status {
	case "merged":
		return settings.Dictionary.Status.Merged
	case "closed":
		return settings.Dictionary.Status.Closed
	default:
		return ""
	}
}

// "%{hoge}" => "{{.hoge}}"
func convertNamedParameters(str string) string {
	re := regexp.MustCompile("%{([^}]+)}")
	return re.ReplaceAllString(str, "{{.$1}}")
}
<commit_msg>:recycle: Refactor<commit_after>package lib

import (
	"bytes"
	"context"
	"fmt"
	"regexp"
	"sort"
	"strings"
	"text/template"

	"github.com/google/go-github/github"
)

// Format is Formatter
type Format struct {
	ctx    context.Context
	client *github.Client
	debug  bool
}

// NewFormat is an initializer
func NewFormat(ctx context.Context, client *github.Client, debug bool) *Format {
	return &Format{ctx: ctx, client: client, debug: debug}
}

// Line is line infomation
type Line struct {
	title    string
	repoName string
	url      string
	user     string
	status   string
}

// NewLineByIssue is an initializer by Issue
func NewLineByIssue(repoName string, issue *github.Issue) Line {
	return Line{
		title:    *issue.Title,
		repoName: repoName,
		url:      *issue.HTMLURL,
		user:     *issue.User.Login,
		status:   getIssueStatus(issue),
	}
}

// NewLineByPullRequest is an initializer by PR
func NewLineByPullRequest(repoName string, pr *github.PullRequest) Line {
	return Line{
		title:    *pr.Title,
		repoName: repoName,
		url:      *pr.HTMLURL,
		user:     *pr.User.Login,
		status:   getPullRequestStatus(pr),
	}
}

// Line returns Issue/PR info retrieving from GitHub
func (f *Format) Line(event *github.Event, i int) Line {
	if f.debug {
		fmt.Printf("%2d %s\n", i, htmlURL(event))
	}

	payload := event.Payload()
	var line Line

	switch *event.Type {
	case "IssuesEvent":
		e := payload.(*github.IssuesEvent)
		issue := getIssue(f.ctx, f.client, *event.Repo.Name, *e.Issue.Number)

		if issue != nil {
			if issue.PullRequestLinks == nil {
				line = NewLineByIssue(*event.Repo.Name, issue)
			} else {
				pr := getPullRequest(f.ctx, f.client, *event.Repo.Name, *e.Issue.Number)
				line = NewLineByPullRequest(*event.Repo.Name, pr)
			}
		} else {
			line = NewLineByIssue(*event.Repo.Name, e.Issue)
		}
	case "IssueCommentEvent":
		e := payload.(*github.IssueCommentEvent)
		issue := getIssue(f.ctx, f.client, *event.Repo.Name, *e.Issue.Number)

		if issue != nil {
			if issue.PullRequestLinks == nil {
				line = NewLineByIssue(*event.Repo.Name, issue)
			} else {
				pr := getPullRequest(f.ctx, f.client, *event.Repo.Name, *e.Issue.Number)
				line = NewLineByPullRequest(*event.Repo.Name, pr)
			}
		} else {
			line = NewLineByIssue(*event.Repo.Name, e.Issue)
		}
	case "PullRequestEvent":
		e := payload.(*github.PullRequestEvent)
		pr := getPullRequest(f.ctx, f.client, *event.Repo.Name, e.GetNumber())
		if pr != nil {
			line = NewLineByPullRequest(*event.Repo.Name, pr)
		} else {
			line = NewLineByPullRequest(*event.Repo.Name, e.PullRequest)
		}
	case "PullRequestReviewCommentEvent":
		e := payload.(*github.PullRequestReviewCommentEvent)
		pr := getPullRequest(f.ctx, f.client, *event.Repo.Name, *e.PullRequest.Number)
		if pr != nil {
			line = NewLineByPullRequest(*event.Repo.Name, pr)
		} else {
			line = NewLineByPullRequest(*event.Repo.Name, e.PullRequest)
		}
	}

	return line
}

func getIssue(ctx context.Context, client *github.Client, repoFullName string, number int) *github.Issue {
	owner, repo := getOwnerRepo(repoFullName)
	issue, _, _ := client.Issues.Get(ctx, owner, repo, number)
	return issue
}

func getPullRequest(ctx context.Context, client *github.Client, repoFullName string, number int) *github.PullRequest {
	owner, repo := getOwnerRepo(repoFullName)
	pr, _, _ := client.PullRequests.Get(ctx, owner, repo, number)
	return pr
}

func getIssueStatus(issue *github.Issue) string {
	result := ""
	if *issue.State == "closed" {
		result = "closed"
	}
	return result
}

func getPullRequestStatus(pr *github.PullRequest) string {
	result := ""
	if *pr.Merged {
		result = "merged"
	} else if *pr.State == "closed" {
		result = "closed"
	}
	return result
}

func getOwnerRepo(repoFullName string) (string, string) {
	s := strings.Split(repoFullName, "/")
	owner := s[0]
	repo := s[1]
	return owner, repo
}

// All returns all lines which are formatted and sorted
func (f *Format) All(lines Lines) (string, error) {
	var result, prevRepoName, currentRepoName string
	var settings Settings

	if err := settings.Init(); err != nil {
		return "", err
	}

	sort.Sort(lines)

	for _, line := range lines {
		currentRepoName = line.repoName

		if currentRepoName != prevRepoName {
			prevRepoName = currentRepoName
			result += fmt.Sprintf("\n%s\n\n", formatSubject(settings, currentRepoName))
		}

		result += fmt.Sprintf("%s\n", formatLine(settings, line))
	}

	return result, nil
}

// Lines has sort.Interface
type Lines []Line

func (l Lines) Len() int {
	return len(l)
}

func (l Lines) Swap(i, j int) {
	l[i], l[j] = l[j], l[i]
}

func (l Lines) Less(i, j int) bool {
	return l[i].url < l[j].url
}

func formatSubject(settings Settings, repoName string) string {
	formatSubject := convertNamedParameters(settings.Format.Subject)

	m := map[string]interface{}{"subject": repoName}
	t := template.Must(template.New("").Parse(formatSubject))

	var rendered bytes.Buffer
	t.Execute(&rendered, m)

	return rendered.String()
}

func formatLine(settings Settings, line Line) string {
	formatLine := convertNamedParameters(settings.Format.Line)

	m := map[string]interface{}{
		"title":  line.title,
		"url":    line.url,
		"user":   line.user,
		"status": formatStatus(settings, line.status),
	}
	t := template.Must(template.New("").Parse(formatLine))

	var rendered bytes.Buffer
	t.Execute(&rendered, m)

	return strings.TrimSpace(rendered.String())
}

func formatStatus(settings Settings, status string) string {
	switch status {
	case "merged":
		return settings.Dictionary.Status.Merged
	case "closed":
		return settings.Dictionary.Status.Closed
	default:
		return ""
	}
}

// "%{hoge}" => "{{.hoge}}"
func convertNamedParameters(str string) string {
	re := regexp.MustCompile("%{([^}]+)}")
	return re.ReplaceAllString(str, "{{.$1}}")
}
<|endoftext|>