<commit_before>package jsoniter

import (
	"fmt"
	"reflect"
	"sort"
	"strings"
	"unicode"
	"unsafe"
)

var typeDecoders = map[string]ValDecoder{}
var fieldDecoders = map[string]ValDecoder{}
var typeEncoders = map[string]ValEncoder{}
var fieldEncoders = map[string]ValEncoder{}
var extensions = []Extension{}

type StructDescriptor struct {
	onePtrEmbedded     bool
	onePtrOptimization bool
	Type               reflect.Type
	Fields             []*Binding
}

func (structDescriptor *StructDescriptor) GetField(fieldName string) *Binding {
	for _, binding := range structDescriptor.Fields {
		if binding.Field.Name == fieldName {
			return binding
		}
	}
	return nil
}

type Binding struct {
	levels    []int
	Field     *reflect.StructField
	FromNames []string
	ToNames   []string
	Encoder   ValEncoder
	Decoder   ValDecoder
}

type Extension interface {
	UpdateStructDescriptor(structDescriptor *StructDescriptor)
	CreateDecoder(typ reflect.Type) ValDecoder
	CreateEncoder(typ reflect.Type) ValEncoder
	DecorateDecoder(typ reflect.Type, decoder ValDecoder) ValDecoder
	DecorateEncoder(typ reflect.Type, encoder ValEncoder) ValEncoder
}

type DummyExtension struct {
}

func (extension *DummyExtension) UpdateStructDescriptor(structDescriptor *StructDescriptor) {
}

func (extension *DummyExtension) CreateDecoder(typ reflect.Type) ValDecoder {
	return nil
}

func (extension *DummyExtension) CreateEncoder(typ reflect.Type) ValEncoder {
	return nil
}

func (extension *DummyExtension) DecorateDecoder(typ reflect.Type, decoder ValDecoder) ValDecoder {
	return decoder
}

func (extension *DummyExtension) DecorateEncoder(typ reflect.Type, encoder ValEncoder) ValEncoder {
	return encoder
}

type funcDecoder struct {
	fun DecoderFunc
}

func (decoder *funcDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
	decoder.fun(ptr, iter)
}

type funcEncoder struct {
	fun         EncoderFunc
	isEmptyFunc func(ptr unsafe.Pointer) bool
}

func (encoder *funcEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
	encoder.fun(ptr, stream)
}

func (encoder *funcEncoder) EncodeInterface(val interface{}, stream *Stream) {
	WriteToStream(val, stream, encoder)
}

func (encoder *funcEncoder) IsEmpty(ptr unsafe.Pointer) bool {
	if encoder.isEmptyFunc == nil {
		return false
	}
	return encoder.isEmptyFunc(ptr)
}

func RegisterTypeDecoderFunc(typ string, fun DecoderFunc) {
	typeDecoders[typ] = &funcDecoder{fun}
}

func RegisterTypeDecoder(typ string, decoder ValDecoder) {
	typeDecoders[typ] = decoder
}

func RegisterFieldDecoderFunc(typ string, field string, fun DecoderFunc) {
	RegisterFieldDecoder(typ, field, &funcDecoder{fun})
}

func RegisterFieldDecoder(typ string, field string, decoder ValDecoder) {
	fieldDecoders[fmt.Sprintf("%s/%s", typ, field)] = decoder
}

func RegisterTypeEncoderFunc(typ string, fun EncoderFunc, isEmptyFunc func(unsafe.Pointer) bool) {
	typeEncoders[typ] = &funcEncoder{fun, isEmptyFunc}
}

func RegisterTypeEncoder(typ string, encoder ValEncoder) {
	typeEncoders[typ] = encoder
}

func RegisterFieldEncoderFunc(typ string, field string, fun EncoderFunc, isEmptyFunc func(unsafe.Pointer) bool) {
	RegisterFieldEncoder(typ, field, &funcEncoder{fun, isEmptyFunc})
}

func RegisterFieldEncoder(typ string, field string, encoder ValEncoder) {
	fieldEncoders[fmt.Sprintf("%s/%s", typ, field)] = encoder
}

func RegisterExtension(extension Extension) {
	extensions = append(extensions, extension)
}

func getTypeDecoderFromExtension(typ reflect.Type) ValDecoder {
	decoder := _getTypeDecoderFromExtension(typ)
	if decoder != nil {
		for _, extension := range extensions {
			decoder = extension.DecorateDecoder(typ, decoder)
		}
	}
	return decoder
}
func _getTypeDecoderFromExtension(typ reflect.Type) ValDecoder {
	for _, extension := range extensions {
		decoder := extension.CreateDecoder(typ)
		if decoder != nil {
			return decoder
		}
	}
	typeName := typ.String()
	decoder := typeDecoders[typeName]
	if decoder != nil {
		return decoder
	}
	if typ.Kind() == reflect.Ptr {
		decoder := typeDecoders[typ.Elem().String()]
		if decoder != nil {
			return &optionalDecoder{typ.Elem(), decoder}
		}
	}
	return nil
}

func getTypeEncoderFromExtension(typ reflect.Type) ValEncoder {
	encoder := _getTypeEncoderFromExtension(typ)
	if encoder != nil {
		for _, extension := range extensions {
			encoder = extension.DecorateEncoder(typ, encoder)
		}
	}
	return encoder
}

func _getTypeEncoderFromExtension(typ reflect.Type) ValEncoder {
	for _, extension := range extensions {
		encoder := extension.CreateEncoder(typ)
		if encoder != nil {
			return encoder
		}
	}
	typeName := typ.String()
	encoder := typeEncoders[typeName]
	if encoder != nil {
		return encoder
	}
	if typ.Kind() == reflect.Ptr {
		encoder := typeEncoders[typ.Elem().String()]
		if encoder != nil {
			return &optionalEncoder{encoder}
		}
	}
	return nil
}

func describeStruct(cfg *frozenConfig, typ reflect.Type) (*StructDescriptor, error) {
	embeddedBindings := []*Binding{}
	bindings := []*Binding{}
	for i := 0; i < typ.NumField(); i++ {
		field := typ.Field(i)
		tag := field.Tag.Get("json")
		tagParts := strings.Split(tag, ",")
		if tag == "-" {
			continue
		}
		if field.Anonymous && (tag == "" || tagParts[0] == "") {
			if field.Type.Kind() == reflect.Struct {
				structDescriptor, err := describeStruct(cfg, field.Type)
				if err != nil {
					return nil, err
				}
				for _, binding := range structDescriptor.Fields {
					binding.levels = append([]int{i}, binding.levels...)
					omitempty := binding.Encoder.(*structFieldEncoder).omitempty
					binding.Encoder = &structFieldEncoder{&field, binding.Encoder, omitempty}
					binding.Decoder = &structFieldDecoder{&field, binding.Decoder}
					embeddedBindings = append(embeddedBindings, binding)
				}
				continue
			} else if field.Type.Kind() == reflect.Ptr && field.Type.Elem().Kind() == reflect.Struct {
				structDescriptor, err := describeStruct(cfg, field.Type.Elem())
				if err != nil {
					return nil, err
				}
				for _, binding := range structDescriptor.Fields {
					binding.levels = append([]int{i}, binding.levels...)
					omitempty := binding.Encoder.(*structFieldEncoder).omitempty
					binding.Encoder = &optionalEncoder{binding.Encoder}
					binding.Encoder = &structFieldEncoder{&field, binding.Encoder, omitempty}
					binding.Decoder = &deferenceDecoder{field.Type.Elem(), binding.Decoder}
					binding.Decoder = &structFieldDecoder{&field, binding.Decoder}
					embeddedBindings = append(embeddedBindings, binding)
				}
				continue
			}
		}
		fieldNames := calcFieldNames(field.Name, tagParts[0], tag)
		fieldCacheKey := fmt.Sprintf("%s/%s", typ.String(), field.Name)
		decoder := fieldDecoders[fieldCacheKey]
		if decoder == nil {
			var err error
			decoder, err = decoderOfType(cfg, field.Type)
			if err != nil {
				return nil, err
			}
		}
		encoder := fieldEncoders[fieldCacheKey]
		if encoder == nil {
			var err error
			encoder, err = encoderOfType(cfg, field.Type)
			if err != nil {
				return nil, err
			}
			// map is stored as pointer in the struct
			if field.Type.Kind() == reflect.Map {
				encoder = &optionalEncoder{encoder}
			}
		}
		binding := &Binding{
			Field:     &field,
			FromNames: fieldNames,
			ToNames:   fieldNames,
			Decoder:   decoder,
			Encoder:   encoder,
		}
		binding.levels = []int{i}
		bindings = append(bindings, binding)
	}
	return createStructDescriptor(cfg, typ, bindings, embeddedBindings), nil
}
func createStructDescriptor(cfg *frozenConfig, typ reflect.Type, bindings []*Binding, embeddedBindings []*Binding) *StructDescriptor {
	onePtrEmbedded := false
	onePtrOptimization := false
	if typ.NumField() == 1 {
		firstField := typ.Field(0)
		switch firstField.Type.Kind() {
		case reflect.Ptr:
			if firstField.Anonymous && firstField.Type.Elem().Kind() == reflect.Struct {
				onePtrEmbedded = true
			}
			fallthrough
		case reflect.Map:
			onePtrOptimization = true
		case reflect.Struct:
			onePtrOptimization = isStructOnePtr(firstField.Type)
		}
	}
	structDescriptor := &StructDescriptor{
		onePtrEmbedded:     onePtrEmbedded,
		onePtrOptimization: onePtrOptimization,
		Type:               typ,
		Fields:             bindings,
	}
	for _, extension := range extensions {
		extension.UpdateStructDescriptor(structDescriptor)
	}
	processTags(structDescriptor, cfg)
	// merge normal & embedded bindings & sort with original order
	allBindings := sortableBindings(append(embeddedBindings, structDescriptor.Fields...))
	sort.Sort(allBindings)
	structDescriptor.Fields = allBindings
	return structDescriptor
}

func isStructOnePtr(typ reflect.Type) bool {
	if typ.NumField() == 1 {
		firstField := typ.Field(0)
		switch firstField.Type.Kind() {
		case reflect.Ptr:
			return true
		case reflect.Map:
			return true
		case reflect.Struct:
			return isStructOnePtr(firstField.Type)
		}
	}
	return false
}

type sortableBindings []*Binding

func (bindings sortableBindings) Len() int {
	return len(bindings)
}

func (bindings sortableBindings) Less(i, j int) bool {
	left := bindings[i].levels
	right := bindings[j].levels
	k := 0
	for {
		if left[k] < right[k] {
			return true
		} else if left[k] > right[k] {
			return false
		}
		k++
	}
}

func (bindings sortableBindings) Swap(i, j int) {
	bindings[i], bindings[j] = bindings[j], bindings[i]
}

func processTags(structDescriptor *StructDescriptor, cfg *frozenConfig) {
	for _, binding := range structDescriptor.Fields {
		shouldOmitEmpty := false
		tagParts := strings.Split(binding.Field.Tag.Get("json"), ",")
		for _, tagPart := range tagParts[1:] {
			if tagPart == "omitempty" {
				shouldOmitEmpty = true
			} else if tagPart == "string" {
				if binding.Field.Type.Kind() == reflect.String {
					binding.Decoder = &stringModeStringDecoder{binding.Decoder, cfg}
					binding.Encoder = &stringModeStringEncoder{binding.Encoder, cfg}
				} else {
					binding.Decoder = &stringModeNumberDecoder{binding.Decoder}
					binding.Encoder = &stringModeNumberEncoder{binding.Encoder}
				}
			}
		}
		binding.Decoder = &structFieldDecoder{binding.Field, binding.Decoder}
		binding.Encoder = &structFieldEncoder{binding.Field, binding.Encoder, shouldOmitEmpty}
	}
}

func calcFieldNames(originalFieldName string, tagProvidedFieldName string, wholeTag string) []string {
	// ignore?
	if wholeTag == "-" {
		return []string{}
	}
	// rename?
	var fieldNames []string
	if tagProvidedFieldName == "" {
		fieldNames = []string{originalFieldName}
	} else {
		fieldNames = []string{tagProvidedFieldName}
	}
	// private?
	isNotExported := unicode.IsLower(rune(originalFieldName[0]))
	if isNotExported {
		fieldNames = []string{}
	}
	return fieldNames
}
<commit_msg>document public symbols<commit_after>package jsoniter

import (
	"fmt"
	"reflect"
	"sort"
	"strings"
	"unicode"
	"unsafe"
)

var typeDecoders = map[string]ValDecoder{}
var fieldDecoders = map[string]ValDecoder{}
var typeEncoders = map[string]ValEncoder{}
var fieldEncoders = map[string]ValEncoder{}
var extensions = []Extension{}

// StructDescriptor describe how should we encode/decode the struct
type StructDescriptor struct {
	onePtrEmbedded     bool
	onePtrOptimization bool
	Type               reflect.Type
	Fields             []*Binding
}

// GetField get one field from the descriptor by its name.
// Can not use map here to keep field orders.
func (structDescriptor *StructDescriptor) GetField(fieldName string) *Binding {
	for _, binding := range structDescriptor.Fields {
		if binding.Field.Name == fieldName {
			return binding
		}
	}
	return nil
}

// Binding describe how should we encode/decode the struct field
type Binding struct {
	levels    []int
	Field     *reflect.StructField
	FromNames []string
	ToNames   []string
	Encoder   ValEncoder
	Decoder   ValDecoder
}

// Extension the one for all SPI. Customize encoding/decoding by specifying alternate encoder/decoder.
// Can also rename fields by UpdateStructDescriptor.
type Extension interface {
	UpdateStructDescriptor(structDescriptor *StructDescriptor)
	CreateDecoder(typ reflect.Type) ValDecoder
	CreateEncoder(typ reflect.Type) ValEncoder
	DecorateDecoder(typ reflect.Type, decoder ValDecoder) ValDecoder
	DecorateEncoder(typ reflect.Type, encoder ValEncoder) ValEncoder
}

// DummyExtension embed this type get dummy implementation for all methods of Extension
type DummyExtension struct {
}

// UpdateStructDescriptor No-op
func (extension *DummyExtension) UpdateStructDescriptor(structDescriptor *StructDescriptor) {
}

// CreateDecoder No-op
func (extension *DummyExtension) CreateDecoder(typ reflect.Type) ValDecoder {
	return nil
}

// CreateEncoder No-op
func (extension *DummyExtension) CreateEncoder(typ reflect.Type) ValEncoder {
	return nil
}

// DecorateDecoder No-op
func (extension *DummyExtension) DecorateDecoder(typ reflect.Type, decoder ValDecoder) ValDecoder {
	return decoder
}

// DecorateEncoder No-op
func (extension *DummyExtension) DecorateEncoder(typ reflect.Type, encoder ValEncoder) ValEncoder {
	return encoder
}

type funcDecoder struct {
	fun DecoderFunc
}

func (decoder *funcDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
	decoder.fun(ptr, iter)
}

type funcEncoder struct {
	fun         EncoderFunc
	isEmptyFunc func(ptr unsafe.Pointer) bool
}

func (encoder *funcEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
	encoder.fun(ptr, stream)
}

func (encoder *funcEncoder) EncodeInterface(val interface{}, stream *Stream) {
	WriteToStream(val, stream, encoder)
}

func (encoder *funcEncoder) IsEmpty(ptr unsafe.Pointer) bool {
	if encoder.isEmptyFunc == nil {
		return false
	}
	return encoder.isEmptyFunc(ptr)
}

// RegisterTypeDecoderFunc register TypeDecoder for a type with function
func RegisterTypeDecoderFunc(typ string, fun DecoderFunc) {
	typeDecoders[typ] = &funcDecoder{fun}
}

// RegisterTypeDecoder register TypeDecoder for a typ
func RegisterTypeDecoder(typ string, decoder ValDecoder) {
	typeDecoders[typ] = decoder
}

// RegisterFieldDecoderFunc register TypeDecoder for a struct field with function
func RegisterFieldDecoderFunc(typ string, field string, fun DecoderFunc) {
	RegisterFieldDecoder(typ, field, &funcDecoder{fun})
}

// RegisterFieldDecoder register TypeDecoder for a struct field
func RegisterFieldDecoder(typ string, field string, decoder ValDecoder) {
	fieldDecoders[fmt.Sprintf("%s/%s", typ, field)] = decoder
}

// RegisterTypeEncoderFunc register TypeEncoder for a type with encode/isEmpty function
func RegisterTypeEncoderFunc(typ string, fun EncoderFunc, isEmptyFunc func(unsafe.Pointer) bool) {
	typeEncoders[typ] = &funcEncoder{fun, isEmptyFunc}
}

// RegisterTypeEncoder register TypeEncoder for a type
func RegisterTypeEncoder(typ string, encoder ValEncoder) {
	typeEncoders[typ] = encoder
}

// RegisterFieldEncoderFunc register TypeEncoder for a struct field with encode/isEmpty function
func RegisterFieldEncoderFunc(typ string, field string, fun EncoderFunc, isEmptyFunc func(unsafe.Pointer) bool) {
	RegisterFieldEncoder(typ, field, &funcEncoder{fun, isEmptyFunc})
}

// RegisterFieldEncoder register TypeEncoder for a struct field
func RegisterFieldEncoder(typ string, field string, encoder ValEncoder) {
	fieldEncoders[fmt.Sprintf("%s/%s", typ, field)] = encoder
}

// RegisterExtension register extension
func RegisterExtension(extension Extension) {
	extensions = append(extensions, extension)
}

func getTypeDecoderFromExtension(typ reflect.Type) ValDecoder {
	decoder := _getTypeDecoderFromExtension(typ)
	if decoder != nil {
		for _, extension := range extensions {
			decoder = extension.DecorateDecoder(typ, decoder)
		}
	}
	return decoder
}
func _getTypeDecoderFromExtension(typ reflect.Type) ValDecoder {
	for _, extension := range extensions {
		decoder := extension.CreateDecoder(typ)
		if decoder != nil {
			return decoder
		}
	}
	typeName := typ.String()
	decoder := typeDecoders[typeName]
	if decoder != nil {
		return decoder
	}
	if typ.Kind() == reflect.Ptr {
		decoder := typeDecoders[typ.Elem().String()]
		if decoder != nil {
			return &optionalDecoder{typ.Elem(), decoder}
		}
	}
	return nil
}

func getTypeEncoderFromExtension(typ reflect.Type) ValEncoder {
	encoder := _getTypeEncoderFromExtension(typ)
	if encoder != nil {
		for _, extension := range extensions {
			encoder = extension.DecorateEncoder(typ, encoder)
		}
	}
	return encoder
}

func _getTypeEncoderFromExtension(typ reflect.Type) ValEncoder {
	for _, extension := range extensions {
		encoder := extension.CreateEncoder(typ)
		if encoder != nil {
			return encoder
		}
	}
	typeName := typ.String()
	encoder := typeEncoders[typeName]
	if encoder != nil {
		return encoder
	}
	if typ.Kind() == reflect.Ptr {
		encoder := typeEncoders[typ.Elem().String()]
		if encoder != nil {
			return &optionalEncoder{encoder}
		}
	}
	return nil
}

func describeStruct(cfg *frozenConfig, typ reflect.Type) (*StructDescriptor, error) {
	embeddedBindings := []*Binding{}
	bindings := []*Binding{}
	for i := 0; i < typ.NumField(); i++ {
		field := typ.Field(i)
		tag := field.Tag.Get("json")
		tagParts := strings.Split(tag, ",")
		if tag == "-" {
			continue
		}
		if field.Anonymous && (tag == "" || tagParts[0] == "") {
			if field.Type.Kind() == reflect.Struct {
				structDescriptor, err := describeStruct(cfg, field.Type)
				if err != nil {
					return nil, err
				}
				for _, binding := range structDescriptor.Fields {
					binding.levels = append([]int{i}, binding.levels...)
					omitempty := binding.Encoder.(*structFieldEncoder).omitempty
					binding.Encoder = &structFieldEncoder{&field, binding.Encoder, omitempty}
					binding.Decoder = &structFieldDecoder{&field, binding.Decoder}
					embeddedBindings = append(embeddedBindings, binding)
				}
				continue
			} else if field.Type.Kind() == reflect.Ptr && field.Type.Elem().Kind() == reflect.Struct {
				structDescriptor, err := describeStruct(cfg, field.Type.Elem())
				if err != nil {
					return nil, err
				}
				for _, binding := range structDescriptor.Fields {
					binding.levels = append([]int{i}, binding.levels...)
					omitempty := binding.Encoder.(*structFieldEncoder).omitempty
					binding.Encoder = &optionalEncoder{binding.Encoder}
					binding.Encoder = &structFieldEncoder{&field, binding.Encoder, omitempty}
					binding.Decoder = &deferenceDecoder{field.Type.Elem(), binding.Decoder}
					binding.Decoder = &structFieldDecoder{&field, binding.Decoder}
					embeddedBindings = append(embeddedBindings, binding)
				}
				continue
			}
		}
		fieldNames := calcFieldNames(field.Name, tagParts[0], tag)
		fieldCacheKey := fmt.Sprintf("%s/%s", typ.String(), field.Name)
		decoder := fieldDecoders[fieldCacheKey]
		if decoder == nil {
			var err error
			decoder, err = decoderOfType(cfg, field.Type)
			if err != nil {
				return nil, err
			}
		}
		encoder := fieldEncoders[fieldCacheKey]
		if encoder == nil {
			var err error
			encoder, err = encoderOfType(cfg, field.Type)
			if err != nil {
				return nil, err
			}
			// map is stored as pointer in the struct
			if field.Type.Kind() == reflect.Map {
				encoder = &optionalEncoder{encoder}
			}
		}
		binding := &Binding{
			Field:     &field,
			FromNames: fieldNames,
			ToNames:   fieldNames,
			Decoder:   decoder,
			Encoder:   encoder,
		}
		binding.levels = []int{i}
		bindings = append(bindings, binding)
	}
	return createStructDescriptor(cfg, typ, bindings, embeddedBindings), nil
}
func createStructDescriptor(cfg *frozenConfig, typ reflect.Type, bindings []*Binding, embeddedBindings []*Binding) *StructDescriptor {
	onePtrEmbedded := false
	onePtrOptimization := false
	if typ.NumField() == 1 {
		firstField := typ.Field(0)
		switch firstField.Type.Kind() {
		case reflect.Ptr:
			if firstField.Anonymous && firstField.Type.Elem().Kind() == reflect.Struct {
				onePtrEmbedded = true
			}
			fallthrough
		case reflect.Map:
			onePtrOptimization = true
		case reflect.Struct:
			onePtrOptimization = isStructOnePtr(firstField.Type)
		}
	}
	structDescriptor := &StructDescriptor{
		onePtrEmbedded:     onePtrEmbedded,
		onePtrOptimization: onePtrOptimization,
		Type:               typ,
		Fields:             bindings,
	}
	for _, extension := range extensions {
		extension.UpdateStructDescriptor(structDescriptor)
	}
	processTags(structDescriptor, cfg)
	// merge normal & embedded bindings & sort with original order
	allBindings := sortableBindings(append(embeddedBindings, structDescriptor.Fields...))
	sort.Sort(allBindings)
	structDescriptor.Fields = allBindings
	return structDescriptor
}

func isStructOnePtr(typ reflect.Type) bool {
	if typ.NumField() == 1 {
		firstField := typ.Field(0)
		switch firstField.Type.Kind() {
		case reflect.Ptr:
			return true
		case reflect.Map:
			return true
		case reflect.Struct:
			return isStructOnePtr(firstField.Type)
		}
	}
	return false
}

type sortableBindings []*Binding

func (bindings sortableBindings) Len() int {
	return len(bindings)
}

func (bindings sortableBindings) Less(i, j int) bool {
	left := bindings[i].levels
	right := bindings[j].levels
	k := 0
	for {
		if left[k] < right[k] {
			return true
		} else if left[k] > right[k] {
			return false
		}
		k++
	}
}

func (bindings sortableBindings) Swap(i, j int) {
	bindings[i], bindings[j] = bindings[j], bindings[i]
}

func processTags(structDescriptor *StructDescriptor, cfg *frozenConfig) {
	for _, binding := range structDescriptor.Fields {
		shouldOmitEmpty := false
		tagParts := strings.Split(binding.Field.Tag.Get("json"), ",")
		for _, tagPart := range tagParts[1:] {
			if tagPart == "omitempty" {
				shouldOmitEmpty = true
			} else if tagPart == "string" {
				if binding.Field.Type.Kind() == reflect.String {
					binding.Decoder = &stringModeStringDecoder{binding.Decoder, cfg}
					binding.Encoder = &stringModeStringEncoder{binding.Encoder, cfg}
				} else {
					binding.Decoder = &stringModeNumberDecoder{binding.Decoder}
					binding.Encoder = &stringModeNumberEncoder{binding.Encoder}
				}
			}
		}
		binding.Decoder = &structFieldDecoder{binding.Field, binding.Decoder}
		binding.Encoder = &structFieldEncoder{binding.Field, binding.Encoder, shouldOmitEmpty}
	}
}

func calcFieldNames(originalFieldName string, tagProvidedFieldName string, wholeTag string) []string {
	// ignore?
	if wholeTag == "-" {
		return []string{}
	}
	// rename?
	var fieldNames []string
	if tagProvidedFieldName == "" {
		fieldNames = []string{originalFieldName}
	} else {
		fieldNames = []string{tagProvidedFieldName}
	}
	// private?
	isNotExported := unicode.IsLower(rune(originalFieldName[0]))
	if isNotExported {
		fieldNames = []string{}
	}
	return fieldNames
}
<|endoftext|>


<commit_before>package filepathfilter_test

import (
	"os"
	"path/filepath"
	"sync"
	"testing"

	"github.com/git-lfs/git-lfs/filepathfilter"
	"github.com/git-lfs/git-lfs/tools"
)

func BenchmarkFilterSimplePath(b *testing.B) {
	files := benchmarkTree(b)
	filter := filepathfilter.New([]string{"lfs"}, nil)
	for i := 0; i < b.N; i++ {
		for _, f := range files {
			filter.Allows(f)
		}
	}
}

func BenchmarkPatternSimplePath(b *testing.B) {
	files := benchmarkTree(b)
	pattern := filepathfilter.NewPattern("lfs")
	for i := 0; i < b.N; i++ {
		for _, f := range files {
			pattern.Match(f)
		}
	}
}

func BenchmarkFilterSimpleExtension(b *testing.B) {
	files := benchmarkTree(b)
	filter := filepathfilter.New([]string{"*.go"}, nil)
	for i := 0; i < b.N; i++ {
		for _, f := range files {
			filter.Allows(f)
		}
	}
}

func BenchmarkPatternSimpleExtension(b *testing.B) {
	files := benchmarkTree(b)
	pattern := filepathfilter.NewPattern("*.go")
	for i := 0; i < b.N; i++ {
		for _, f := range files {
			pattern.Match(f)
		}
	}
}

func BenchmarkFilterComplexExtension(b *testing.B) {
	files := benchmarkTree(b)
	filter := filepathfilter.New([]string{"*.travis.yml"}, nil)
	for i := 0; i < b.N; i++ {
		for _, f := range files {
			filter.Allows(f)
		}
	}
}

func BenchmarkPatternComplexExtension(b *testing.B) {
	files := benchmarkTree(b)
	pattern := filepathfilter.NewPattern("*.travis.yml")
	for i := 0; i < b.N; i++ {
		for _, f := range files {
			pattern.Match(f)
		}
	}
}

func BenchmarkFilterDoubleAsterisk(b *testing.B) {
	files := benchmarkTree(b)
	filter := filepathfilter.New([]string{"**/README.md"}, nil)
	for i := 0; i < b.N; i++ {
		for _, f := range files {
			filter.Allows(f)
		}
	}
}

func BenchmarkPatternDoubleAsterisk(b *testing.B) {
	files := benchmarkTree(b)
	pattern := filepathfilter.NewPattern("**/README.md")
	for i := 0; i < b.N; i++ {
		for _, f := range files {
			pattern.Match(f)
		}
	}
}

var (
	benchmarkFiles []string
	benchmarkMu    sync.Mutex
)

func benchmarkTree(b *testing.B) []string {
	benchmarkMu.Lock()
	defer benchmarkMu.Unlock()

	if benchmarkFiles != nil {
		return benchmarkFiles
	}

	wd, err := os.Getwd()
	if err != nil {
		b.Fatal(err)
	}

	hasErrors := false
	tools.FastWalkGitRepo(filepath.Dir(wd), func(parent string, info os.FileInfo, err error) {
		if err != nil {
			hasErrors = true
			b.Error(err)
			return
		}
		benchmarkFiles = append(benchmarkFiles, filepath.Join(parent, info.Name()))
	})

	if hasErrors {
		b.Fatal("has errors :(")
	}

	return benchmarkFiles
}
<commit_msg>filepathfilter: remove outdated benchmark<commit_after><|endoftext|>


<commit_before>package unifi

import "log"

type ConfigNetwork struct {
	Ip   string
	Type string
}

type DownlinkTable struct {
	/* FIXME */
}

type RadioTable struct {
	Antenna_gain     int `json:"-"` // FIXME: buggy input, sometimes string and sometimes int
	Builtin_ant_gain int
	Builtin_antenna  bool
	Channel          string
	Ht               string
	Max_txpower      int
	Mode             string
	Name             string
	Radio            string
	Tx_power         string
	Tx_power_mode    string
}

type Uplink struct {
	Full_duplex  bool
	Ip           string
	Mac          string
	Name         string
	Num_port     int
	Rx_bytes     int
	Rx_dropped   int
	Rx_errors    int
	Rx_multicast int
	Rx_packets   int
	Speed        int
	Tx_bytes     int
	Tx_dropped   int
	Tx_errors    int
	Tx_packets   int
	Type         string
	Up           bool
}

type UplinkTable struct {
	/* FIXME */
}

type VapTable struct {
	Ap_mac      string
	Bssid       string
	Ccq         int
	Channel     int
	Essid       string
	Id          string
	Is_guest    bool
	Is_wep      bool
	Map_id      string
	Name        string
	Num_sta     int
	Radio       string
	Rx_bytes    int
	Rx_crypts   int
	Rx_dropped  int
	Rx_errors   int
	Rx_frags    int
	Rx_nwids    int
	Rx_packets  int
	Sta_table   []StaTable
	State       string
	T           string
	Tx_bytes    int
	Tx_dropped  int
	Tx_errors   int
	Tx_packets  int
	Tx_power    int
	Tx_retries  int
	Up          bool
	Usage       string
	Wlanconf_id string
}

type StaTable struct {
	Auth_time      int
	Authorized     bool
	Ccq            int
	Dhcpend_time   int
	Dhcpstart_time int
	Hostname       string
	Idletime       int
	Ip             string
	Is_11b         bool
	Is_11n         bool
	Mac            string
	Noise          int
	Rssi           int
	Rx_bytes       int
	Rx_packets     int
	Rx_rate        int
	Signal         int
	State          int
	State_ht       bool
	State_pwrmgt   bool
	Tx_bytes       int
	Tx_packets     int
	Tx_power       int
	Tx_rate        int
	Uptime         int
}

type VwireTable struct {
	/* FIXME */
}

type Stat struct {
	Ap    string
	Bytes int
	/*
		Na-num_sta  int
		Na-rx_bytes  int
		Na-rx_frags  int
		Na-rx_packets  int
		Na-time_delta  int
		Na-tx_bytes  int
		Na-tx_errors  int
		Na-tx_packets  int
		Na-tx_retries  int
		Ng-num_sta  int
		Ng-rx_bytes  int
		Ng-rx_frags  int
		Ng-rx_packets  int
		Ng-time_delta  int
		Ng-tx_bytes  int
		Ng-tx_errors  int
		Ng-tx_packets  int
		Ng-tx_retries  int
	*/
	Num_sta    int
	O          string
	Rx_bytes   int
	Rx_frags   int
	Rx_packets int
	Time_delta int
	Tx_bytes   int
	Tx_errors  int
	Tx_packets int
	Tx_retries int
	Type       string
	/*
		Uplink-rx_bytes  int
		Uplink-rx_packets  int
		Uplink-time_delta  int
		Uplink-tx_bytes  int
		Uplink-tx_packets  int
		User-na-num_sta  int
		User-na-rx_bytes  int
		User-na-rx_frags  int
		User-na-rx_packets  int
		User-na-time_delta  int
		User-na-tx_bytes  int
		User-na-tx_errors  int
		User-na-tx_packets  int
		User-na-tx_retries  int
		User-ng-num_sta  int
		User-ng-rx_bytes  int
		User-ng-rx_frags  int
		User-ng-rx_packets  int
		User-ng-time_delta  int
		User-ng-tx_bytes  int
		User-ng-tx_errors  int
		User-ng-tx_packets  int
		User-ng-tx_retries  int
		User-num_sta  int
		User-rx_bytes  int
		User-rx_frags  int
		User-rx_packets  int
		User-time_delta  int
		User-tx_bytes  int
		User-tx_errors  int
		User-tx_packets  int
		User-tx_retries  int
	*/
}

type ApsMap map[string]Aps

// Access point datA
type Aps struct {
	u                    *Unifi
	Adopted              bool
	Bytes                int
	Cfgversion           string
	Config_network       ConfigNetwork
	Connect_request_ip   string
	Connect_request_port string
	Considered_lost_at   int
	Downlink_table       []DownlinkTable
	Guest_num_sta        int `json:"guest-num_sta"`
	Guest_token          string
	Has_eth1             bool
	Has_poe_passthrough  bool
	Inform_authkey       string
	Inform_ip            string
	Inform_url           string
	Ip                   string
	Known_cfgversion     string
	Last_seen            int
	Locating             bool
	Locked               bool
	Mac                  string
	Map_id               string
	Model                string
	Na_channel           int    `json:"na-channel"`
	Na_eirp              int    `json:"na-eirp"`
	Na_extchannel        int    `json:"na-extchannel"`
	Na_gain              int    `json:"na-gain"`
	Na_state             string `json:"na-state"`
	Na_tx_power          string `json:"na-tx_power"`
	Name                 string
	Next_heartbeat_at    int
	Ng_channel           int    `json:"ng-channel"`
	Ng_eirp              int    `json:"ng-eirp"`
	Ng_extchannel        int    `json:"ng-extchannel"`
	Ng_gain              int    `json:"ng-gain"`
	Ng_state             string `json:"ng-state"`
	Ng_tx_power          string `json:"ng-tx_power"`
	Num_sta              int
	Radio_table          []RadioTable
	Rx_bytes             int
	Scanning             bool
	Serial               string
	Stat                 Stat
	State                int
	Tx_bytes             int
	Uplink               Uplink
	Uplink_table         []UplinkTable
	Uptime               int
	User_num_sta         int `json:"user-num_sta"`
	Vap_table            []VapTable
	Version              string
	VwireEnabled         bool
	Vwire_table          []VwireTable
	X                    int
	X_authkey            string
	X_fingerprint        string
	X_vwirekey           string
	Y                    int
}

// Reboot access point
func (a Aps) Restart() {
	if a.u == nil {
		log.Fatal("login first")
	}
	a.u.devcmd(a.Mac, "restart")
}

var model = map[string]string{
	"BZ2":   "UniFi AP",
	"BZ2LR": "UniFi AP-LR",
	"U2S48": "UniFi AP",
	"U2L48": "UniFi AP-LR",
	"U2HSR": "UniFi AP-Outdoor+",
	"U2O":   "UniFi AP-Outdoor",
	"U5O":   "UniFi AP-Outdoor 5G",
	"U7P":   "UniFi AP-Pro",
	"U2M":   "UniFi AP-Mini",
	"p2N":   "PicoStation M2",
	"U7E":   "UniFi AP-AC",
	"U7O":   "UniFi AP-AC Outdoor",
	"U7Ev2": "UniFi AP-AC v2",
}

func (a Aps) ModelName() string {
	if m, ok := model[a.Model]; ok {
		return m
	}
	return "unknown"
}

var status = map[int]string{
	0:  "Disconnected",
	1:  "Connected",
	2:  "Pending",
	3:  "Connected",
	4:  "Upgrading",
	5:  "Provisioning",
	6:  "HeartbeatMissed",
	7:  "Adoping",
	8:  "Deleting",
	9:  "ManagedByOthers",
	10: "AdoptFailed",
	11: "Isolated",
	12: "IsolatePending",
	13: "WirelessAdopting",
}

func (a Aps) Status() string {
	if s, ok := status[a.State]; ok {
		return s
	}
	return "unknown"
}
<commit_msg>fix status<commit_after>package unifi

import "log"

type ConfigNetwork struct {
	Ip   string
	Type string
}

type DownlinkTable struct {
	/* FIXME */
}

type RadioTable struct {
	Antenna_gain     int `json:"-"` // FIXME: buggy input, sometimes string and sometimes int
	Builtin_ant_gain int
	Builtin_antenna  bool
	Channel          string
	Ht               string
	Max_txpower      int
	Mode             string
	Name             string
	Radio            string
	Tx_power         string
	Tx_power_mode    string
}

type Uplink struct {
	Full_duplex  bool
	Ip           string
	Mac          string
	Name         string
	Num_port     int
	Rx_bytes     int
	Rx_dropped   int
	Rx_errors    int
	Rx_multicast int
	Rx_packets   int
	Speed        int
	Tx_bytes     int
	Tx_dropped   int
	Tx_errors    int
	Tx_packets   int
	Type         string
	Up           bool
}

type UplinkTable struct {
	/* FIXME */
}

type VapTable struct {
	Ap_mac      string
	Bssid       string
	Ccq         int
	Channel     int
	Essid       string
	Id          string
	Is_guest    bool
	Is_wep      bool
	Map_id      string
	Name        string
	Num_sta     int
	Radio       string
	Rx_bytes    int
	Rx_crypts   int
	Rx_dropped  int
	Rx_errors   int
	Rx_frags    int
	Rx_nwids    int
	Rx_packets  int
	Sta_table   []StaTable
	State       string
	T           string
	Tx_bytes    int
	Tx_dropped  int
	Tx_errors   int
	Tx_packets  int
	Tx_power    int
	Tx_retries  int
	Up          bool
	Usage       string
	Wlanconf_id string
}

type StaTable struct {
	Auth_time      int
	Authorized     bool
	Ccq            int
	Dhcpend_time   int
	Dhcpstart_time int
	Hostname       string
	Idletime       int
	Ip             string
	Is_11b         bool
	Is_11n         bool
	Mac            string
	Noise          int
	Rssi           int
	Rx_bytes       int
	Rx_packets     int
	Rx_rate        int
	Signal         int
	State          int
	State_ht       bool
	State_pwrmgt   bool
	Tx_bytes       int
	Tx_packets     int
	Tx_power       int
	Tx_rate        int
	Uptime         int
}

type VwireTable struct {
	/* FIXME */
}

type Stat struct {
	Ap    string
	Bytes int
	/*
		Na-num_sta  int
		Na-rx_bytes  int
		Na-rx_frags  int
		Na-rx_packets  int
		Na-time_delta  int
		Na-tx_bytes  int
		Na-tx_errors  int
		Na-tx_packets  int
		Na-tx_retries  int
		Ng-num_sta  int
		Ng-rx_bytes  int
		Ng-rx_frags  int
		Ng-rx_packets  int
		Ng-time_delta  int
		Ng-tx_bytes  int
		Ng-tx_errors  int
		Ng-tx_packets  int
		Ng-tx_retries  int
	*/
	Num_sta    int
	O          string
	Rx_bytes   int
	Rx_frags   int
	Rx_packets int
	Time_delta int
	Tx_bytes   int
	Tx_errors  int
	Tx_packets int
	Tx_retries int
	Type       string
	/*
		Uplink-rx_bytes  int
		Uplink-rx_packets  int
		Uplink-time_delta  int
		Uplink-tx_bytes  int
		Uplink-tx_packets  int
		User-na-num_sta  int
		User-na-rx_bytes  int
		User-na-rx_frags  int
		User-na-rx_packets  int
		User-na-time_delta  int
		User-na-tx_bytes  int
		User-na-tx_errors  int
		User-na-tx_packets  int
		User-na-tx_retries  int
		User-ng-num_sta  int
		User-ng-rx_bytes  int
		User-ng-rx_frags  int
		User-ng-rx_packets  int
		User-ng-time_delta  int
		User-ng-tx_bytes  int
		User-ng-tx_errors  int
		User-ng-tx_packets  int
		User-ng-tx_retries  int
		User-num_sta  int
		User-rx_bytes  int
		User-rx_frags  int
		User-rx_packets  int
		User-time_delta  int
		User-tx_bytes  int
		User-tx_errors  int
		User-tx_packets  int
		User-tx_retries  int
	*/
}

type ApsMap map[string]Aps

// Access point datA
type Aps struct {
	u                    *Unifi
	Adopted              bool
	Bytes                int
	Cfgversion           string
	Config_network       ConfigNetwork
	Connect_request_ip   string
	Connect_request_port string
	Considered_lost_at   int
	Downlink_table       []DownlinkTable
	Guest_num_sta        int `json:"guest-num_sta"`
	Guest_token          string
	Has_eth1             bool
	Has_poe_passthrough  bool
	Inform_authkey       string
	Inform_ip            string
	Inform_url           string
	Ip                   string
	Known_cfgversion     string
	Last_seen            int
	Locating             bool
	Locked               bool
	Mac                  string
	Map_id               string
	Model                string
	Na_channel           int    `json:"na-channel"`
	Na_eirp              int    `json:"na-eirp"`
	Na_extchannel        int    `json:"na-extchannel"`
	Na_gain              int    `json:"na-gain"`
	Na_state             string `json:"na-state"`
	Na_tx_power          string `json:"na-tx_power"`
	Name                 string
	Next_heartbeat_at    int
	Ng_channel           int    `json:"ng-channel"`
	Ng_eirp              int    `json:"ng-eirp"`
	Ng_extchannel        int    `json:"ng-extchannel"`
	Ng_gain              int    `json:"ng-gain"`
	Ng_state             string `json:"ng-state"`
	Ng_tx_power          string `json:"ng-tx_power"`
	Num_sta              int
	Radio_table          []RadioTable
	Rx_bytes             int
	Scanning             bool
	Serial               string
	Stat                 Stat
	State                int
	Tx_bytes             int
	Uplink               Uplink
	Uplink_table         []UplinkTable
	Uptime               int
	User_num_sta         int `json:"user-num_sta"`
	Vap_table            []VapTable
	Version              string
	VwireEnabled         bool
	Vwire_table          []VwireTable
	X                    int
	X_authkey            string
	X_fingerprint        string
	X_vwirekey           string
	Y                    int
}

// Reboot access point
func (a Aps) Restart() {
	if a.u == nil {
		log.Fatal("login first")
	}
	a.u.devcmd(a.Mac, "restart")
}

var model = map[string]string{
	"BZ2":   "UniFi AP",
	"BZ2LR": "UniFi AP-LR",
	"U2S48": "UniFi AP",
	"U2L48": "UniFi AP-LR",
	"U2HSR": "UniFi AP-Outdoor+",
	"U2O":   "UniFi AP-Outdoor",
	"U5O":   "UniFi AP-Outdoor 5G",
	"U7P":   "UniFi AP-Pro",
	"U2M":   "UniFi AP-Mini",
	"p2N":   "PicoStation M2",
	"U7E":   "UniFi AP-AC",
	"U7O":   "UniFi AP-AC Outdoor",
	"U7Ev2": "UniFi AP-AC v2",
}

func (a Aps) ModelName() string {
	if m, ok := model[a.Model]; ok {
		return m
	}
	return "unknown"
}

var status = map[int]string{
	0:  "Disconnected",
	1:  "Connected",
	2:  "Pending",
	3:  "Disconnected",
	4:  "Upgrading",
	5:  "Provisioning",
	6:  "HeartbeatMissed",
	7:  "Adoping",
	8:  "Deleting",
	9:  "ManagedByOthers",
	10: "AdoptFailed",
	11: "Isolated",
	12: "IsolatePending",
	13: "WirelessAdopting",
}

func (a Aps) Status() string {
	if s, ok := status[a.State]; ok {
		return s
	}
	return "unknown"
}
<|endoftext|>


<commit_before>package jigo

import (
	"bytes"
	"fmt"
	"strconv"
)

var textFormat = "%s" // Changed to "%q" in tests for better error messages.

type NodeType int

func (t NodeType) Type() NodeType {
	return t
}

type Node interface {
	Type() NodeType
	String() string
	// Copy does a deep copy of the Node and all its components.
	Copy() Node
	Position() Pos // byte position of start of node in full original input string
}

const (
	NodeList NodeType = iota
	NodeText
	NodeVar
	NodeLookup
	NodeUnary
	NodeFloat
	NodeInteger
	NodeString
	NodeAdd
	NodeMul
	NodeMapExpr
	NodeMapElem
	NodeIndexExpr
	NodeSet
	NodeIf
	NodeFor
)

// This is a stack of nodes starting at a position.  It has the default NodeType
// but should never end up in the AST;  it's use is in implementing order of
// operations for expressions
type nodeStack struct {
	NodeType
	Pos
	Nodes []Node
}

func newStack(pos Pos) *nodeStack {
	return &nodeStack{Pos: pos}
}

func (n *nodeStack) len() int       { return len(n.Nodes) }
func (n *nodeStack) push(node Node) { n.Nodes = append(n.Nodes, node) }
func (n *nodeStack) pop() Node {
	var r Node
	if len(n.Nodes) > 0 {
		r = n.Nodes[len(n.Nodes)-1]
		n.Nodes = n.Nodes[:len(n.Nodes)-1]
	}
	return r
}

// ListNode holds a sequence of nodes.
type ListNode struct {
	NodeType
	Pos
	Nodes []Node // The element nodes in lexical order.
}

func newList(pos Pos) *ListNode {
	return &ListNode{NodeType: NodeList, Pos: pos}
}

func (l *ListNode) append(n Node) { l.Nodes = append(l.Nodes, n) }
func (l *ListNode) len() int      { return len(l.Nodes) }

func (l *ListNode) String() string {
	b := new(bytes.Buffer)
	for _, n := range l.Nodes {
		fmt.Fprint(b, n)
	}
	return b.String()
}

func (l *ListNode) CopyList() *ListNode {
	if l == nil {
		return l
	}
	n := newList(l.Pos)
	for _, elem := range l.Nodes {
		n.append(elem.Copy())
	}
	return n
}

func (l *ListNode) Copy() Node { return l.CopyList() }

// TextNode holds plain text.
type TextNode struct {
	NodeType
	Pos
	Text []byte // The text; may span newlines.
}

func newText(pos Pos, text string) *TextNode {
	return &TextNode{NodeType: NodeText, Pos: pos, Text: []byte(text)}
}

func (t *TextNode) String() string { return fmt.Sprintf(textFormat, t.Text) }
func (t *TextNode) Copy() Node     { return &TextNode{NodeText, t.Pos, append([]byte{}, t.Text...)} }

// VarNode represents a var print expr, ie {{ ... }}.
// It is represented as a sequence of expressions.
type VarNode struct {
	NodeType
	Pos
	Node Node
}

func newVar(pos Pos) *VarNode {
	return &VarNode{NodeType: NodeVar, Pos: pos}
}

func (v *VarNode) String() string { return "{{ " + v.Node.String() + " }}" }
func (v *VarNode) Copy() Node     { return &VarNode{v.NodeType, v.Pos, v.Node} }

// A LookupNode is a variable lookup.
type LookupNode struct {
	NodeType
	Pos
	Name string
}

func newLookup(pos Pos, name string) *LookupNode {
	return &LookupNode{NodeType: NodeLookup, Pos: pos, Name: name}
}
func (l *LookupNode) String() string { return l.Name }
func (l *LookupNode) Copy() Node     { return newLookup(l.Pos, l.Name) }

type StringNode struct {
	NodeType
	Pos
	Value string
}

func (s *StringNode) Copy() Node     { return &StringNode{s.NodeType, s.Pos, s.Value} }
func (s *StringNode) String() string { return fmt.Sprintf(`"%s"`, s.Value) }

type IntegerNode struct {
	NodeType
	Pos
	Value int64
}

func (i *IntegerNode) Copy() Node     { return &IntegerNode{i.NodeType, i.Pos, i.Value} }
func (i *IntegerNode) String() string { return strconv.FormatInt(i.Value, 10) }

type FloatNode struct {
	NodeType
	Pos
	Value float64
}

func (f *FloatNode) Copy() Node     { return &FloatNode{f.NodeType, f.Pos, f.Value} }
func (f *FloatNode) String() string { return fmt.Sprint(f.Value) }

type UnaryNode struct {
	NodeType
	Pos
	Value Node
	Unary item
}

func newUnaryNode(val Node, unary item) *UnaryNode {
	return &UnaryNode{NodeUnary, val.Position(), val, unary}
}

func (u *UnaryNode) Copy() Node     { return &UnaryNode{u.NodeType, u.Pos, u.Value, u.Unary} }
func (u *UnaryNode) String() string { return fmt.Sprintf("%s%s", u.Unary.val, u.Value) }

// newLiteral creates a new string, integer, or float node depending on itemType
func newLiteral(pos Pos, typ itemType, val string) Node {
	switch typ {
	case tokenFloat:
		v, err := strconv.ParseFloat(val, 64)
		if err != nil {
			panic(err)
		}
		return &FloatNode{NodeFloat, pos, v}
	case tokenInteger:
		// FIXME: complex integer types?  hex, octal, etc?
		v, err := strconv.ParseInt(val, 10, 64)
		if err != nil {
			panic(err)
		}
		return &IntegerNode{NodeInteger, pos, v}
	case tokenString:
		return &StringNode{NodeString, pos, val}
	}
	panic(fmt.Sprint("unexpected literal type ", typ))
}

type AddExpr struct {
	NodeType
	Pos
	lhs      Node
	rhs      Node
	operator item
}

func newAddExpr(lhs, rhs Node, operator item) *AddExpr {
	return &AddExpr{NodeAdd, lhs.Position(), lhs, rhs, operator}
}

func (a *AddExpr) String() string {
	return fmt.Sprintf("%s %s %s", a.lhs, a.operator.val, a.rhs)
}

func (a *AddExpr) Copy() Node {
	return newAddExpr(a.lhs, a.rhs, a.operator)
}

type MulExpr struct {
	NodeType
	Pos
	lhs      Node
	rhs      Node
	operator item
}

func newMulExpr(lhs, rhs Node, operator item) *MulExpr {
	return &MulExpr{NodeMul, lhs.Position(), lhs, rhs, operator}
}

func (m *MulExpr) String() string {
	return fmt.Sprintf("%s %s %s", m.lhs, m.operator.val, m.rhs)
}

func (m *MulExpr) Copy() Node {
	return newMulExpr(m.lhs, m.rhs, m.operator)
}

// complex literals

type MapExpr struct {
	NodeType
	Pos
	Elems []*MapElem
}

func newMapExpr(pos Pos) *MapExpr {
	return &MapExpr{NodeType: NodeMapExpr, Pos: pos}
}

func (m *MapExpr) len() int          { return len(m.Elems) }
func (m *MapExpr) append(n *MapElem) { m.Elems = append(m.Elems, n) }

func (m *MapExpr) String() string {
	b := new(bytes.Buffer)
	b.WriteString("{")
	for i, n := range m.Elems {
		fmt.Fprint(b, n)
		if i != len(m.Elems)-1 {
			b.WriteString(", ")
		}
	}
	b.WriteString("}")
	return b.String()
}

func (m *MapExpr) Copy() Node {
	if m == nil {
		return m
	}
	n := newMapExpr(m.Pos)
	for _, elem := range m.Elems {
		n.append(elem.Copy().(*MapElem))
	}
	return n
}

type MapElem struct {
	NodeType
	Pos
	Key   Node
	Value Node
}

func newMapElem(lhs, rhs Node) *MapElem {
	return &MapElem{NodeMapElem, lhs.Position(), lhs, rhs}
}

func (m *MapElem) String() string {
	return fmt.Sprintf("%s: %s", m.Key, m.Value)
}

func (m *MapElem) Copy() Node {
	return newMapElem(m.Key, m.Value)
}

type IndexExpr struct {
	NodeType
	Pos
	Value Node
	Index Node
}

func newIndexExpr(val, idx Node) *IndexExpr {
	return &IndexExpr{NodeIndexExpr, val.Position(), val, idx}
}

func (i *IndexExpr) String() string {
	return fmt.Sprintf("%s[%s}", i.Value, i.Index)
}

func (i *IndexExpr) Copy() Node {
	return newIndexExpr(i.Value, i.Index)
}

// block types
type SetNode struct {
	NodeType
	Pos
	lhs Node
	rhs Node
}

func newSet(pos Pos, lhs, rhs Node) *SetNode {
	return &SetNode{NodeSet, pos, lhs, rhs}
}

func (s *SetNode) String() string { return fmt.Sprintf("set %s = %s", s.lhs, s.rhs) }
func (s *SetNode) Copy() Node {
	return newSet(s.Pos, s.lhs.Copy(), s.rhs.Copy())
}

type IfNode struct {
	NodeType
	Pos
	Guard Node
	Body  Node
	Elifs []Node
	Else  Node
}

func newIf(pos Pos) *IfNode {
	return &IfNode{NodeType: NodeIf, Pos: pos}
}

func (i *IfNode) String() string { return "if" }
func (i *IfNode) Copy() Node {
	n := newIf(i.Pos)
	n.Guard = i.Guard.Copy()
	n.Body = i.Body.Copy()
	n.Elifs = make([]Node, len(i.Elifs))
	for _, e := range i.Elifs {
		n.Elifs = append(n.Elifs, e.Copy())
	}
	n.Else = i.Else.Copy()
	return n
}

type ForNode struct {
	NodeType
	Pos
	ForExpr Node
	InExpr  Node
	Body    Node
}

func newFor(pos Pos) *ForNode {
	return &ForNode{NodeType: NodeFor, Pos: pos}
}

func (f *ForNode) String() string {
	return fmt.Sprintf("{% for %s in %s %}%s{% endfor %}", f.ForExpr, f.InExpr, f.Body)
}
func (f *ForNode) Copy() Node {
	n := newFor(f.Pos)
	n.ForExpr = f.ForExpr.Copy()
	n.InExpr = f.InExpr.Copy()
	n.Body = f.Body.Copy()
	return n
}
<commit_msg>add a block stub<commit_after>package jigo

import (
	"bytes"
	"fmt"
	"strconv"
)

var textFormat = "%s" // Changed to "%q" in tests for better error messages.

type NodeType int

func (t NodeType) Type() NodeType {
	return t
}

type Node interface {
	Type() NodeType
	String() string
	// Copy does a deep copy of the Node and all its components.
	Copy() Node
	Position() Pos // byte position of start of node in full original input string
}

const (
	NodeList NodeType = iota
	NodeText
	NodeVar
	NodeLookup
	NodeUnary
	NodeFloat
	NodeInteger
	NodeString
	NodeAdd
	NodeMul
	NodeMapExpr
	NodeMapElem
	NodeIndexExpr
	NodeSet
	NodeIf
	NodeFor
)

// This is a stack of nodes starting at a position.  It has the default NodeType
// but should never end up in the AST;  it's use is in implementing order of
// operations for expressions
type nodeStack struct {
	NodeType
	Pos
	Nodes []Node
}

func newStack(pos Pos) *nodeStack {
	return &nodeStack{Pos: pos}
}

func (n *nodeStack) len() int       { return len(n.Nodes) }
func (n *nodeStack) push(node Node) { n.Nodes = append(n.Nodes, node) }
func (n *nodeStack) pop() Node {
	var r Node
	if len(n.Nodes) > 0 {
		r = n.Nodes[len(n.Nodes)-1]
		n.Nodes = n.Nodes[:len(n.Nodes)-1]
	}
	return r
}

// ListNode holds a sequence of nodes.
type ListNode struct {
	NodeType
	Pos
	Nodes []Node // The element nodes in lexical order.
}

func newList(pos Pos) *ListNode {
	return &ListNode{NodeType: NodeList, Pos: pos}
}

func (l *ListNode) append(n Node) { l.Nodes = append(l.Nodes, n) }
func (l *ListNode) len() int      { return len(l.Nodes) }

func (l *ListNode) String() string {
	b := new(bytes.Buffer)
	for _, n := range l.Nodes {
		fmt.Fprint(b, n)
	}
	return b.String()
}

func (l *ListNode) CopyList() *ListNode {
	if l == nil {
		return l
	}
	n := newList(l.Pos)
	for _, elem := range l.Nodes {
		n.append(elem.Copy())
	}
	return n
}

func (l *ListNode) Copy() Node { return l.CopyList() }

// TextNode holds plain text.
type TextNode struct {
	NodeType
	Pos
	Text []byte // The text; may span newlines.
}

func newText(pos Pos, text string) *TextNode {
	return &TextNode{NodeType: NodeText, Pos: pos, Text: []byte(text)}
}

func (t *TextNode) String() string { return fmt.Sprintf(textFormat, t.Text) }
func (t *TextNode) Copy() Node     { return &TextNode{NodeText, t.Pos, append([]byte{}, t.Text...)} }

// VarNode represents a var print expr, ie {{ ... }}.
// It is represented as a sequence of expressions.
type VarNode struct {
	NodeType
	Pos
	Node Node
}

func newVar(pos Pos) *VarNode {
	return &VarNode{NodeType: NodeVar, Pos: pos}
}

func (v *VarNode) String() string { return "{{ " + v.Node.String() + " }}" }
func (v *VarNode) Copy() Node     { return &VarNode{v.NodeType, v.Pos, v.Node} }

// A LookupNode is a variable lookup.
type LookupNode struct {
	NodeType
	Pos
	Name string
}

func newLookup(pos Pos, name string) *LookupNode {
	return &LookupNode{NodeType: NodeLookup, Pos: pos, Name: name}
}
func (l *LookupNode) String() string { return l.Name }
func (l *LookupNode) Copy() Node     { return newLookup(l.Pos, l.Name) }

type StringNode struct {
	NodeType
	Pos
	Value string
}

func (s *StringNode) Copy() Node     { return &StringNode{s.NodeType, s.Pos, s.Value} }
func (s *StringNode) String() string { return fmt.Sprintf(`"%s"`, s.Value) }

type IntegerNode struct {
	NodeType
	Pos
	Value int64
}

func (i *IntegerNode) Copy() Node     { return &IntegerNode{i.NodeType, i.Pos, i.Value} }
func (i *IntegerNode) String() string { return strconv.FormatInt(i.Value, 10) }

type FloatNode struct {
	NodeType
	Pos
	Value float64
}

func (f *FloatNode) Copy() Node     { return &FloatNode{f.NodeType, f.Pos, f.Value} }
func (f *FloatNode) String() string { return fmt.Sprint(f.Value) }

type UnaryNode struct {
	NodeType
	Pos
	Value Node
	Unary item
}

func newUnaryNode(val Node, unary item) *UnaryNode {
	return &UnaryNode{NodeUnary, val.Position(), val, unary}
}

func (u *UnaryNode) Copy() Node     { return &UnaryNode{u.NodeType, u.Pos, u.Value, u.Unary} }
func (u *UnaryNode) String() string { return fmt.Sprintf("%s%s", u.Unary.val, u.Value) }

// newLiteral creates a new string, integer, or float node depending on itemType
func newLiteral(pos Pos, typ itemType, val string) Node {
	switch typ {
	case tokenFloat:
		v, err := strconv.ParseFloat(val, 64)
		if err != nil {
			panic(err)
		}
		return &FloatNode{NodeFloat, pos, v}
	case tokenInteger:
		// FIXME: complex integer types?  hex, octal, etc?
		v, err := strconv.ParseInt(val, 10, 64)
		if err != nil {
			panic(err)
		}
		return &IntegerNode{NodeInteger, pos, v}
	case tokenString:
		return &StringNode{NodeString, pos, val}
	}
	panic(fmt.Sprint("unexpected literal type ", typ))
}

type AddExpr struct {
	NodeType
	Pos
	lhs      Node
	rhs      Node
	operator item
}

func newAddExpr(lhs, rhs Node, operator item) *AddExpr {
	return &AddExpr{NodeAdd, lhs.Position(), lhs, rhs, operator}
}

func (a *AddExpr) String() string {
	return fmt.Sprintf("%s %s %s", a.lhs, a.operator.val, a.rhs)
}

func (a *AddExpr) Copy() Node {
	return newAddExpr(a.lhs, a.rhs, a.operator)
}

type MulExpr struct {
	NodeType
	Pos
	lhs      Node
	rhs      Node
	operator item
}

func newMulExpr(lhs, rhs Node, operator item) *MulExpr {
	return &MulExpr{NodeMul, lhs.Position(), lhs, rhs, operator}
}

func (m *MulExpr) String() string {
	return fmt.Sprintf("%s %s %s", m.lhs, m.operator.val, m.rhs)
}

func (m *MulExpr) Copy() Node {
	return newMulExpr(m.lhs, m.rhs, m.operator)
}

// complex literals

type MapExpr struct {
	NodeType
	Pos
	Elems []*MapElem
}

func newMapExpr(pos Pos) *MapExpr {
	return &MapExpr{NodeType: NodeMapExpr, Pos: pos}
}

func (m *MapExpr) len() int          { return len(m.Elems) }
func (m *MapExpr) append(n *MapElem) { m.Elems = append(m.Elems, n) }

func (m *MapExpr) String() string {
	b := new(bytes.Buffer)
	b.WriteString("{")
	for i, n := range m.Elems {
		fmt.Fprint(b, n)
		if i != len(m.Elems)-1 {
			b.WriteString(", ")
		}
	}
	b.WriteString("}")
	return b.String()
}

func (m *MapExpr) Copy() Node {
	if m == nil {
		return m
	}
	n := newMapExpr(m.Pos)
	for _, elem := range m.Elems {
		n.append(elem.Copy().(*MapElem))
	}
	return n
}

type MapElem struct {
	NodeType
	Pos
	Key   Node
	Value Node
}

func newMapElem(lhs, rhs Node) *MapElem {
	return &MapElem{NodeMapElem, lhs.Position(), lhs, rhs}
}

func (m *MapElem) String() string {
	return fmt.Sprintf("%s: %s", m.Key, m.Value)
}

func (m *MapElem) Copy() Node {
	return newMapElem(m.Key, m.Value)
}

type IndexExpr struct {
	NodeType
	Pos
	Value Node
	Index Node
}

func newIndexExpr(val, idx Node) *IndexExpr {
	return &IndexExpr{NodeIndexExpr, val.Position(), val, idx}
}

func (i *IndexExpr) String() string {
	return fmt.Sprintf("%s[%s}", i.Value, i.Index)
}

func (i *IndexExpr) Copy() Node {
	return newIndexExpr(i.Value, i.Index)
}

// block types
type SetNode struct {
	NodeType
	Pos
	lhs Node
	rhs Node
}

func newSet(pos Pos, lhs, rhs Node) *SetNode {
	return &SetNode{NodeSet, pos, lhs, rhs}
}

func (s *SetNode) String() string { return fmt.Sprintf("set %s = %s", s.lhs, s.rhs) }
func (s *SetNode) Copy() Node {
	return newSet(s.Pos, s.lhs.Copy(), s.rhs.Copy())
}

type IfNode struct {
	NodeType
	Pos
	Guard Node
	Body  Node
	Elifs []Node
	Else  Node
}

func newIf(pos Pos) *IfNode {
	return &IfNode{NodeType: NodeIf, Pos: pos}
}

func (i *IfNode) String() string { return "if" }
func (i *IfNode) Copy() Node {
	n := newIf(i.Pos)
	n.Guard = i.Guard.Copy()
	n.Body = i.Body.Copy()
	n.Elifs = make([]Node, len(i.Elifs))
	for _, e := range i.Elifs {
		n.Elifs = append(n.Elifs, e.Copy())
	}
	n.Else = i.Else.Copy()
	return n
}

type ForNode struct {
	NodeType
	Pos
	ForExpr Node
	InExpr  Node
	Body    Node
}

func newFor(pos Pos) *ForNode {
	return &ForNode{NodeType: NodeFor, Pos: pos}
}

func (f *ForNode) String() string {
	return fmt.Sprintf("{% for %s in %s %}%s{% endfor %}", f.ForExpr, f.InExpr, f.Body)
}
func (f *ForNode) Copy() Node {
	n := newFor(f.Pos)
	n.ForExpr = f.ForExpr.Copy()
	n.InExpr = f.InExpr.Copy()
	n.Body = f.Body.Copy()
	return n
}

type BlockNode struct {
	NodeType
	Pos
	Name string
	Body Node
}

func (b *BlockNode) String() string {
	return fmt.Sprintf("{% block %s %}%s{% endblock %}", b.Name, b.Body)
}

func (b *BlockNode) Copy() Node {

}
<|endoftext|>


<commit_before>package telebot

import (
	"encoding/json"
	"fmt"
	"net/url"
	"strconv"
	"time"
)

// Bot represents a separate Telegram bot instance.
type Bot struct {
	Token string

	// Bot as `User` on API level.
	Identity User
}

// NewBot does try to build a Bot with token `token`, which
// is a secret API key assigned to particular bot.
func NewBot(token string) (*Bot, error) {
	user, err := getMe(token)
	if err != nil {
		return nil, err
	}

	return &Bot{
		Token:    token,
		Identity: user,
	}, nil
}

// Listen periodically looks for updates and delivers new messages
// to subscription channel.
func (b Bot) Listen(subscription chan<- Message, interval time.Duration) {
	updates := make(chan Update)
	pulse := time.NewTicker(interval)
	latestUpdate := 0

	go func() {
		for range pulse.C {
			go getUpdates(b.Token,
				latestUpdate+1,
				updates)
		}
	}()

	go func() {
		for update := range updates {
			if update.ID > latestUpdate {
				latestUpdate = update.ID
			}

			subscription <- update.Payload
		}
	}()
}

// SendMessage sends a text message to recipient.
func (b Bot) SendMessage(recipient User, message string, options *SendOptions) error {
	params := url.Values{}
	params.Set("chat_id", strconv.Itoa(recipient.ID))
	params.Set("text", message)

	if options != nil {
		embedSendOptions(&params, options)
	}

	responseJSON, err := sendCommand("sendMessage", b.Token, params)
	if err != nil {
		return err
	}

	var responseRecieved struct {
		Ok          bool
		Description string
	}

	err = json.Unmarshal(responseJSON, &responseRecieved)
	if err != nil {
		return err
	}

	if !responseRecieved.Ok {
		return SendError{responseRecieved.Description}
	}

	return nil
}

// ForwardMessage forwards a message to recipient.
func (b Bot) ForwardMessage(recipient User, message Message) error {
	params := url.Values{}
	params.Set("chat_id", strconv.Itoa(recipient.ID))
	params.Set("from_chat_id", strconv.Itoa(message.Origin().ID))
	params.Set("message_id", strconv.Itoa(message.ID))

	responseJSON, err := sendCommand("forwardMessage", b.Token, params)
	if err != nil {
		return err
	}

	var responseRecieved struct {
		Ok          bool
		Description string
	}

	err = json.Unmarshal(responseJSON, &responseRecieved)
	if err != nil {
		return err
	}

	if !responseRecieved.Ok {
		return SendError{responseRecieved.Description}
	}

	return nil
}

// SendPhoto sends a photo object to recipient.
//
// On success, photo object would be aliased to its copy on
// the Telegram servers, so sending the same photo object
// again, won't issue a new upload, but would make a use
// of existing file on Telegram servers.
func (b Bot) SendPhoto(recipient User, photo *Photo, options *SendOptions) error {
	params := url.Values{}
	params.Set("chat_id", strconv.Itoa(recipient.ID))
	params.Set("caption", photo.Caption)

	if options != nil {
		embedSendOptions(&params, options)
	}

	var responseJSON []byte
	var err error

	if photo.Exists() {
		params.Set("photo", photo.FileID)
		responseJSON, err = sendCommand("sendPhoto", b.Token, params)
	} else {
		responseJSON, err = sendFile("sendPhoto", b.Token, "photo",
			photo.filename, params)
	}

	if err != nil {
		return err
	}

	var responseRecieved struct {
		Ok          bool
		Result      Message
		Description string
	}

	err = json.Unmarshal(responseJSON, &responseRecieved)
	if err != nil {
		return err
	}

	if !responseRecieved.Ok {
		return SendError{responseRecieved.Description}
	}

	thumbnails := &responseRecieved.Result.Photo
	filename := photo.filename
	photo.File = (*thumbnails)[len(*thumbnails)-1].File
	photo.filename = filename

	return nil
}

// SendAudio sends an audio object to recipient.
//
// On success, audio object would be aliased to its copy on
// the Telegram servers, so sending the same audio object
// again, won't issue a new upload, but would make a use
// of existing file on Telegram servers.
func (b Bot) SendAudio(recipient User, audio *Audio, options *SendOptions) error {
	params := url.Values{}
	params.Set("chat_id", strconv.Itoa(recipient.ID))

	if options != nil {
		embedSendOptions(&params, options)
	}

	var responseJSON []byte
	var err error

	if audio.Exists() {
		params.Set("audio", audio.FileID)
		responseJSON, err = sendCommand("sendAudio", b.Token, params)
	} else {
		responseJSON, err = sendFile("sendAudio", b.Token, "audio",
			audio.filename, params)
	}

	if err != nil {
		return err
	}

	var responseRecieved struct {
		Ok          bool
		Result      Message
		Description string
	}

	err = json.Unmarshal(responseJSON, &responseRecieved)
	if err != nil {
		return err
	}

	if !responseRecieved.Ok {
		return SendError{responseRecieved.Description}
	}

	filename := audio.filename
	*audio = responseRecieved.Result.Audio
	audio.filename = filename

	return nil
}

// SendDocument sends a general document object to recipient.
//
// On success, document object would be aliased to its copy on
// the Telegram servers, so sending the same document object
// again, won't issue a new upload, but would make a use
// of existing file on Telegram servers.
func (b Bot) SendDocument(recipient User, doc *Document, options *SendOptions) error {
	params := url.Values{}
	params.Set("chat_id", strconv.Itoa(recipient.ID))

	if options != nil {
		embedSendOptions(&params, options)
	}

	var responseJSON []byte
	var err error

	if doc.Exists() {
		params.Set("document", doc.FileID)
		responseJSON, err = sendCommand("sendDocument", b.Token, params)
	} else {
		responseJSON, err = sendFile("sendDocument", b.Token, "document",
			doc.filename, params)
	}

	if err != nil {
		return err
	}

	var responseRecieved struct {
		Ok          bool
		Result      Message
		Description string
	}

	err = json.Unmarshal(responseJSON, &responseRecieved)
	if err != nil {
		return err
	}

	if !responseRecieved.Ok {
		return SendError{responseRecieved.Description}
	}

	filename := doc.filename
	*doc = responseRecieved.Result.Document
	doc.filename = filename

	return nil
}

// SendSticker sends a general document object to recipient.
//
// On success, sticker object would be aliased to its copy on
// the Telegram servers, so sending the same sticker object
// again, won't issue a new upload, but would make a use
// of existing file on Telegram servers.
func (b *Bot) SendSticker(recipient User, sticker *Sticker, options *SendOptions) error {
	params := url.Values{}
	params.Set("chat_id", strconv.Itoa(recipient.ID))

	if options != nil {
		embedSendOptions(&params, options)
	}

	var responseJSON []byte
	var err error

	if sticker.Exists() {
		params.Set("sticker", sticker.FileID)
		responseJSON, err = sendCommand("sendSticker", b.Token, params)
	} else {
		responseJSON, err = sendFile("sendSticker", b.Token, "sticker",
			sticker.filename, params)
	}

	if err != nil {
		return err
	}

	var responseRecieved struct {
		Ok          bool
		Result      Message
		Description string
	}

	err = json.Unmarshal(responseJSON, &responseRecieved)
	if err != nil {
		return err
	}

	if !responseRecieved.Ok {
		return SendError{responseRecieved.Description}
	}

	filename := sticker.filename
	*sticker = responseRecieved.Result.Sticker
	sticker.filename = filename

	return nil
}

// SendVideo sends a general document object to recipient.
//
// On success, video object would be aliased to its copy on
// the Telegram servers, so sending the same video object
// again, won't issue a new upload, but would make a use
// of existing file on Telegram servers.
func (b Bot) SendVideo(recipient User, video *Video, options *SendOptions) error {
	params := url.Values{}
	params.Set("chat_id", strconv.Itoa(recipient.ID))

	if options != nil {
		embedSendOptions(&params, options)
	}

	var responseJSON []byte
	var err error

	if video.Exists() {
		params.Set("video", video.FileID)
		responseJSON, err = sendCommand("sendVideo", b.Token, params)
	} else {
		responseJSON, err = sendFile("sendVideo", b.Token, "video",
			video.filename, params)
	}

	if err != nil {
		return err
	}

	var responseRecieved struct {
		Ok          bool
		Result      Message
		Description string
	}

	err = json.Unmarshal(responseJSON, &responseRecieved)
	if err != nil {
		return err
	}

	if !responseRecieved.Ok {
		return SendError{responseRecieved.Description}
	}

	filename := video.filename
	*video = responseRecieved.Result.Video
	video.filename = filename

	return nil
}

// SendLocation sends a general document object to recipient.
//
// On success, video object would be aliased to its copy on
// the Telegram servers, so sending the same video object
// again, won't issue a new upload, but would make a use
// of existing file on Telegram servers.
func (b Bot) SendLocation(recipient User, geo *Location, options *SendOptions) error {
	params := url.Values{}
	params.Set("chat_id", strconv.Itoa(recipient.ID))
	params.Set("latitude", fmt.Sprintf("%f", geo.Latitude))
	params.Set("longitude", fmt.Sprintf("%f", geo.Longitude))

	if options != nil {
		embedSendOptions(&params, options)
	}

	responseJSON, err := sendCommand("sendLocation", b.Token, params)

	if err != nil {
		return err
	}

	var responseRecieved struct {
		Ok          bool
		Result      Message
		Description string
	}

	err = json.Unmarshal(responseJSON, &responseRecieved)
	if err != nil {
		return err
	}

	if !responseRecieved.Ok {
		return SendError{responseRecieved.Description}
	}

	return nil
}

// SendChatAction updates a chat action for recipient.
//
// Chat action is a status message that recipient would see where
// you typically see "Harry is typing" status message. The only
// difference is that bots' chat actions live only for 5 seconds
// and die just once the client recieves a message from the bot.
//
// Currently, Telegram supports only a narrow range of possible
// actions, these are aligned as constants of this package.
func (b Bot) SendChatAction(recipient User, action string) error {
	params := url.Values{}
	params.Set("chat_id", strconv.Itoa(recipient.ID))
	params.Set("action", action)

	responseJSON, err := sendCommand("sendChatAction", b.Token, params)

	if err != nil {
		return err
	}

	var responseRecieved struct {
		Ok          bool
		Description string
	}

	err = json.Unmarshal(responseJSON, &responseRecieved)
	if err != nil {
		return err
	}

	if !responseRecieved.Ok {
		return SendError{responseRecieved.Description}
	}

	return nil
}
<commit_msg>Easy older go compat<commit_after>package telebot

import (
	"encoding/json"
	"fmt"
	"net/url"
	"strconv"
	"time"
)

// Bot represents a separate Telegram bot instance.
type Bot struct {
	Token string

	// Bot as `User` on API level.
	Identity User
}

// NewBot does try to build a Bot with token `token`, which
// is a secret API key assigned to particular bot.
func NewBot(token string) (*Bot, error) {
	user, err := getMe(token)
	if err != nil {
		return nil, err
	}

	return &Bot{
		Token:    token,
		Identity: user,
	}, nil
}

// Listen periodically looks for updates and delivers new messages
// to subscription channel.
func (b Bot) Listen(subscription chan<- Message, interval time.Duration) {
	updates := make(chan Update)
	pulse := time.NewTicker(interval)
	latestUpdate := 0

	go func() {
		for _ = range pulse.C {
			go getUpdates(b.Token,
				latestUpdate+1,
				updates)
		}
	}()

	go func() {
		for update := range updates {
			if update.ID > latestUpdate {
				latestUpdate = update.ID
			}

			subscription <- update.Payload
		}
	}()
}

// SendMessage sends a text message to recipient.
func (b Bot) SendMessage(recipient User, message string, options *SendOptions) error {
	params := url.Values{}
	params.Set("chat_id", strconv.Itoa(recipient.ID))
	params.Set("text", message)

	if options != nil {
		embedSendOptions(&params, options)
	}

	responseJSON, err := sendCommand("sendMessage", b.Token, params)
	if err != nil {
		return err
	}

	var responseRecieved struct {
		Ok          bool
		Description string
	}

	err = json.Unmarshal(responseJSON, &responseRecieved)
	if err != nil {
		return err
	}

	if !responseRecieved.Ok {
		return SendError{responseRecieved.Description}
	}

	return nil
}

// ForwardMessage forwards a message to recipient.
func (b Bot) ForwardMessage(recipient User, message Message) error {
	params := url.Values{}
	params.Set("chat_id", strconv.Itoa(recipient.ID))
	params.Set("from_chat_id", strconv.Itoa(message.Origin().ID))
	params.Set("message_id", strconv.Itoa(message.ID))

	responseJSON, err := sendCommand("forwardMessage", b.Token, params)
	if err != nil {
		return err
	}

	var responseRecieved struct {
		Ok          bool
		Description string
	}

	err = json.Unmarshal(responseJSON, &responseRecieved)
	if err != nil {
		return err
	}

	if !responseRecieved.Ok {
		return SendError{responseRecieved.Description}
	}

	return nil
}

// SendPhoto sends a photo object to recipient.
//
// On success, photo object would be aliased to its copy on
// the Telegram servers, so sending the same photo object
// again, won't issue a new upload, but would make a use
// of existing file on Telegram servers.
func (b Bot) SendPhoto(recipient User, photo *Photo, options *SendOptions) error {
	params := url.Values{}
	params.Set("chat_id", strconv.Itoa(recipient.ID))
	params.Set("caption", photo.Caption)

	if options != nil {
		embedSendOptions(&params, options)
	}

	var responseJSON []byte
	var err error

	if photo.Exists() {
		params.Set("photo", photo.FileID)
		responseJSON, err = sendCommand("sendPhoto", b.Token, params)
	} else {
		responseJSON, err = sendFile("sendPhoto", b.Token, "photo",
			photo.filename, params)
	}

	if err != nil {
		return err
	}

	var responseRecieved struct {
		Ok          bool
		Result      Message
		Description string
	}

	err = json.Unmarshal(responseJSON, &responseRecieved)
	if err != nil {
		return err
	}

	if !responseRecieved.Ok {
		return SendError{responseRecieved.Description}
	}

	thumbnails := &responseRecieved.Result.Photo
	filename := photo.filename
	photo.File = (*thumbnails)[len(*thumbnails)-1].File
	photo.filename = filename

	return nil
}

// SendAudio sends an audio object to recipient.
//
// On success, audio object would be aliased to its copy on
// the Telegram servers, so sending the same audio object
// again, won't issue a new upload, but would make a use
// of existing file on Telegram servers.
func (b Bot) SendAudio(recipient User, audio *Audio, options *SendOptions) error {
	params := url.Values{}
	params.Set("chat_id", strconv.Itoa(recipient.ID))

	if options != nil {
		embedSendOptions(&params, options)
	}

	var responseJSON []byte
	var err error

	if audio.Exists() {
		params.Set("audio", audio.FileID)
		responseJSON, err = sendCommand("sendAudio", b.Token, params)
	} else {
		responseJSON, err = sendFile("sendAudio", b.Token, "audio",
			audio.filename, params)
	}

	if err != nil {
		return err
	}

	var responseRecieved struct {
		Ok          bool
		Result      Message
		Description string
	}

	err = json.Unmarshal(responseJSON, &responseRecieved)
	if err != nil {
		return err
	}

	if !responseRecieved.Ok {
		return SendError{responseRecieved.Description}
	}

	filename := audio.filename
	*audio = responseRecieved.Result.Audio
	audio.filename = filename

	return nil
}

// SendDocument sends a general document object to recipient.
//
// On success, document object would be aliased to its copy on
// the Telegram servers, so sending the same document object
// again, won't issue a new upload, but would make a use
// of existing file on Telegram servers.
func (b Bot) SendDocument(recipient User, doc *Document, options *SendOptions) error {
	params := url.Values{}
	params.Set("chat_id", strconv.Itoa(recipient.ID))

	if options != nil {
		embedSendOptions(&params, options)
	}

	var responseJSON []byte
	var err error

	if doc.Exists() {
		params.Set("document", doc.FileID)
		responseJSON, err = sendCommand("sendDocument", b.Token, params)
	} else {
		responseJSON, err = sendFile("sendDocument", b.Token, "document",
			doc.filename, params)
	}

	if err != nil {
		return err
	}

	var responseRecieved struct {
		Ok          bool
		Result      Message
		Description string
	}

	err = json.Unmarshal(responseJSON, &responseRecieved)
	if err != nil {
		return err
	}

	if !responseRecieved.Ok {
		return SendError{responseRecieved.Description}
	}

	filename := doc.filename
	*doc = responseRecieved.Result.Document
	doc.filename = filename

	return nil
}

// SendSticker sends a general document object to recipient.
//
// On success, sticker object would be aliased to its copy on
// the Telegram servers, so sending the same sticker object
// again, won't issue a new upload, but would make a use
// of existing file on Telegram servers.
func (b *Bot) SendSticker(recipient User, sticker *Sticker, options *SendOptions) error {
	params := url.Values{}
	params.Set("chat_id", strconv.Itoa(recipient.ID))

	if options != nil {
		embedSendOptions(&params, options)
	}

	var responseJSON []byte
	var err error

	if sticker.Exists() {
		params.Set("sticker", sticker.FileID)
		responseJSON, err = sendCommand("sendSticker", b.Token, params)
	} else {
		responseJSON, err = sendFile("sendSticker", b.Token, "sticker",
			sticker.filename, params)
	}

	if err != nil {
		return err
	}

	var responseRecieved struct {
		Ok          bool
		Result      Message
		Description string
	}

	err = json.Unmarshal(responseJSON, &responseRecieved)
	if err != nil {
		return err
	}

	if !responseRecieved.Ok {
		return SendError{responseRecieved.Description}
	}

	filename := sticker.filename
	*sticker = responseRecieved.Result.Sticker
	sticker.filename = filename

	return nil
}

// SendVideo sends a general document object to recipient.
//
// On success, video object would be aliased to its copy on
// the Telegram servers, so sending the same video object
// again, won't issue a new upload, but would make a use
// of existing file on Telegram servers.
func (b Bot) SendVideo(recipient User, video *Video, options *SendOptions) error {
	params := url.Values{}
	params.Set("chat_id", strconv.Itoa(recipient.ID))

	if options != nil {
		embedSendOptions(&params, options)
	}

	var responseJSON []byte
	var err error

	if video.Exists() {
		params.Set("video", video.FileID)
		responseJSON, err = sendCommand("sendVideo", b.Token, params)
	} else {
		responseJSON, err = sendFile("sendVideo", b.Token, "video",
			video.filename, params)
	}

	if err != nil {
		return err
	}

	var responseRecieved struct {
		Ok          bool
		Result      Message
		Description string
	}

	err = json.Unmarshal(responseJSON, &responseRecieved)
	if err != nil {
		return err
	}

	if !responseRecieved.Ok {
		return SendError{responseRecieved.Description}
	}

	filename := video.filename
	*video = responseRecieved.Result.Video
	video.filename = filename

	return nil
}

// SendLocation sends a general document object to recipient.
//
// On success, video object would be aliased to its copy on
// the Telegram servers, so sending the same video object
// again, won't issue a new upload, but would make a use
// of existing file on Telegram servers.
func (b Bot) SendLocation(recipient User, geo *Location, options *SendOptions) error {
	params := url.Values{}
	params.Set("chat_id", strconv.Itoa(recipient.ID))
	params.Set("latitude", fmt.Sprintf("%f", geo.Latitude))
	params.Set("longitude", fmt.Sprintf("%f", geo.Longitude))

	if options != nil {
		embedSendOptions(&params, options)
	}

	responseJSON, err := sendCommand("sendLocation", b.Token, params)

	if err != nil {
		return err
	}

	var responseRecieved struct {
		Ok          bool
		Result      Message
		Description string
	}

	err = json.Unmarshal(responseJSON, &responseRecieved)
	if err != nil {
		return err
	}

	if !responseRecieved.Ok {
		return SendError{responseRecieved.Description}
	}

	return nil
}

// SendChatAction updates a chat action for recipient.
//
// Chat action is a status message that recipient would see where
// you typically see "Harry is typing" status message. The only
// difference is that bots' chat actions live only for 5 seconds
// and die just once the client recieves a message from the bot.
//
// Currently, Telegram supports only a narrow range of possible
// actions, these are aligned as constants of this package.
func (b Bot) SendChatAction(recipient User, action string) error {
	params := url.Values{}
	params.Set("chat_id", strconv.Itoa(recipient.ID))
	params.Set("action", action)

	responseJSON, err := sendCommand("sendChatAction", b.Token, params)

	if err != nil {
		return err
	}

	var responseRecieved struct {
		Ok          bool
		Description string
	}

	err = json.Unmarshal(responseJSON, &responseRecieved)
	if err != nil {
		return err
	}

	if !responseRecieved.Ok {
		return SendError{responseRecieved.Description}
	}

	return nil
}
<|endoftext|>


<commit_before>package slack

import (
	"net/http"
	"net/url"

	log "github.com/Sirupsen/logrus"
	"github.com/gorilla/websocket"
)

const (
	// Version is the semantic version of this library.
	Version = "0.1.1"
)

// Bot encapsulates all the data needed to interact with Slack.
type Bot struct {
	Token       string
	Name        string
	ID          string
	Handlers    map[string]([]BotAction)
	Subhandlers map[string](map[string]([]BotAction))
	Users       map[string]string
	Channels    map[string]string
}

// NewBot constructs a new bot with the passed-in Slack API token.
func NewBot(token string) *Bot {
	return &Bot{
		Token:       token,
		Name:        "",
		ID:          "",
		Handlers:    make(map[string]([]BotAction)),
		Subhandlers: make(map[string](map[string]([]BotAction))),
		Users:       make(map[string]string),
		Channels:    make(map[string]string),
	}
}

// Start initiates the bot's interaction with Slack. It obtains a websockect
// URL, connects to it, and then starts the main loop.
func (bot *Bot) Start() error {
	payload, err := bot.Call("rtm.start", url.Values{})
	if err != nil {
		return err
	}
	success, ok := payload["ok"].(bool)
	if !(ok && success) {
		return &Error{"could not connect to RTM API"}
	}
	websocketURL, _ := payload["url"].(string)
	self := payload["self"].(map[string]interface{})
	channels := payload["channels"].([]interface{})
	for _, channelMap := range channels {
		channel := channelMap.(map[string]interface{})
		channelID := channel["id"].(string)
		channelName := channel["name"].(string)
		bot.Channels[channelName] = channelID
		bot.Channels[channelID] = channelName
	}
	users := payload["users"].([]interface{})
	for _, userMap := range users {
		user := userMap.(map[string]interface{})
		userID := user["id"].(string)
		userName := user["name"].(string)
		bot.Users[userName] = userID
		bot.Users[userID] = userName
	}
	bot.Name = self["name"].(string)
	bot.ID = self["id"].(string)
	return bot.connect(websocketURL)
}

func (bot *Bot) connect(websocketURL string) error {
	dialer := websocket.Dialer{}
	conn, _, err := dialer.Dial(websocketURL, http.Header{})
	if err != nil {
		return err
	}
	bot.loop(conn)
	return nil
}

func (bot *Bot) loop(conn *websocket.Conn) {
	for {
		messageType, bytes, err := conn.ReadMessage()
		if err != nil {
			// ReadMessage returns an error if the connection is closed
			conn.Close()
			return
		}
		if messageType == websocket.BinaryMessage {
			continue // ignore binary messages
		}
		event, err := unpackEvent(bytes)
		if err != nil {
			log.WithFields(log.Fields{
				"raw bytes": bytes,
				"error":     err,
			}).Warn("message could not be unpacked")
		}
		wrappers := bot.handle(event)
		closeConnection := sendResponses(wrappers, conn)
		if closeConnection {
			conn.Close()
			return
		}
	}
}

func sendResponses(wrappers []messageWrapper, conn *websocket.Conn) bool {
	abort := false
	for _, wrapper := range wrappers {
		message := wrapper.message
		switch wrapper.status {
		case Continue:
			if message != nil {
				conn.WriteJSON(message.toMap())
			}
		case Shutdown:
			if message != nil {
				conn.WriteJSON(message.toMap())
			}
			abort = true
		case ShutdownNow:
			return true
		}
	}
	return abort
}
<commit_msg>add missing continue, and an info log<commit_after>package slack

import (
	"net/http"
	"net/url"

	log "github.com/Sirupsen/logrus"
	"github.com/gorilla/websocket"
)

const (
	// Version is the semantic version of this library.
	Version = "0.1.1"
)

// Bot encapsulates all the data needed to interact with Slack.
type Bot struct {
	Token       string
	Name        string
	ID          string
	Handlers    map[string]([]BotAction)
	Subhandlers map[string](map[string]([]BotAction))
	Users       map[string]string
	Channels    map[string]string
}

// NewBot constructs a new bot with the passed-in Slack API token.
func NewBot(token string) *Bot {
	return &Bot{
		Token:       token,
		Name:        "",
		ID:          "",
		Handlers:    make(map[string]([]BotAction)),
		Subhandlers: make(map[string](map[string]([]BotAction))),
		Users:       make(map[string]string),
		Channels:    make(map[string]string),
	}
}

// Start initiates the bot's interaction with Slack. It obtains a websockect
// URL, connects to it, and then starts the main loop.
func (bot *Bot) Start() error {
	payload, err := bot.Call("rtm.start", url.Values{})
	if err != nil {
		return err
	}
	success, ok := payload["ok"].(bool)
	if !(ok && success) {
		return &Error{"could not connect to RTM API"}
	}
	websocketURL, _ := payload["url"].(string)
	self := payload["self"].(map[string]interface{})
	channels := payload["channels"].([]interface{})
	for _, channelMap := range channels {
		channel := channelMap.(map[string]interface{})
		channelID := channel["id"].(string)
		channelName := channel["name"].(string)
		bot.Channels[channelName] = channelID
		bot.Channels[channelID] = channelName
	}
	users := payload["users"].([]interface{})
	for _, userMap := range users {
		user := userMap.(map[string]interface{})
		userID := user["id"].(string)
		userName := user["name"].(string)
		bot.Users[userName] = userID
		bot.Users[userID] = userName
	}
	bot.Name = self["name"].(string)
	bot.ID = self["id"].(string)
	return bot.connect(websocketURL)
}

func (bot *Bot) connect(websocketURL string) error {
	dialer := websocket.Dialer{}
	conn, _, err := dialer.Dial(websocketURL, http.Header{})
	if err != nil {
		return err
	}
	bot.loop(conn)
	return nil
}

func (bot *Bot) loop(conn *websocket.Conn) {
	for {
		messageType, bytes, err := conn.ReadMessage()
		if err != nil {
			// ReadMessage returns an error if the connection is closed
			conn.Close()
			return
		}
		if messageType == websocket.BinaryMessage {
			continue // ignore binary messages
		}
		event, err := unpackEvent(bytes)
		if err != nil {
			log.WithFields(log.Fields{
				"raw bytes": bytes,
				"error":     err,
			}).Warn("message could not be unpacked")
			continue
		}
		log.WithFields(log.Fields{
			"event": event,
		}).Info("received event")
		wrappers := bot.handle(event)
		closeConnection := sendResponses(wrappers, conn)
		if closeConnection {
			conn.Close()
			return
		}
	}
}

func sendResponses(wrappers []messageWrapper, conn *websocket.Conn) bool {
	abort := false
	for _, wrapper := range wrappers {
		message := wrapper.message
		switch wrapper.status {
		case Continue:
			if message != nil {
				conn.WriteJSON(message.toMap())
			}
		case Shutdown:
			if message != nil {
				conn.WriteJSON(message.toMap())
			}
			abort = true
		case ShutdownNow:
			return true
		}
	}
	return abort
}
<|endoftext|>


<commit_before>package rice

import (
	"bytes"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"github.com/GeertJohan/go.rice/embedded"
)

// Box abstracts a directory for resources/files.
// It can either load files from disk, or from embedded code (when `rice --embed` was ran).
type Box struct {
	name         string
	absolutePath string
	embed        *embedded.EmbeddedBox
	appendd      *appendedBox
}

var defaultLocateOrder = []LocateMethod{LocateEmbedded, LocateAppended, LocateFS}

func findBox(name string, order []LocateMethod) (*Box, error) {
	b := &Box{name: name}

	// no support for absolute paths since gopath can be different on different machines.
	// therefore, required box must be located relative to package requiring it.
	if filepath.IsAbs(name) {
		return nil, errors.New("given name/path is absolute")
	}

	var err error
	for _, method := range order {
		switch method {
		case LocateEmbedded:
			if embed := embedded.EmbeddedBoxes[name]; embed != nil {
				b.embed = embed
				return b, nil
			}

		case LocateAppended:
			appendedBoxName := strings.Replace(name, `/`, `-`, -1)
			if appendd := appendedBoxes[appendedBoxName]; appendd != nil {
				b.appendd = appendd
				return b, nil
			}

		case LocateFS:
			// resolve absolute directory path
			err := b.resolveAbsolutePathFromCaller()
			if err != nil {
				continue
			}
			// check if absolutePath exists on filesystem
			info, err := os.Stat(b.absolutePath)
			if err != nil {
				continue
			}
			// check if absolutePath is actually a directory
			if !info.IsDir() {
				err = errors.New("given name/path is not a directory")
				continue
			}
			return b, nil
		}
	}

	if err == nil {
		err = fmt.Errorf("could not locate box %q", name)
	}

	return nil, err
}

// FindBox returns a Box instance for given name.
// When the given name is a relative path, it's base path will be the calling pkg/cmd's source root.
// When the given name is absolute, it's absolute. derp.
// Make sure the path doesn't contain any sensitive information as it might be placed into generated go source (embedded).
func FindBox(name string) (*Box, error) {
	return findBox(name, defaultLocateOrder)
}

// MustFindBox returns a Box instance for given name, like FindBox does.
// It does not return an error, instead it panics when an error occurs.
func MustFindBox(name string) *Box {
	box, err := findBox(name, defaultLocateOrder)
	if err != nil {
		panic(err)
	}
	return box
}

// This is injected as a mutable function literal so that we can mock it out in
// tests and return a fixed test file.
var resolveAbsolutePathFromCaller = func(name string, nStackFrames int) (string, error) {
	_, callingGoFile, _, ok := runtime.Caller(nStackFrames)
	if !ok {
		return "", errors.New("couldn't find caller on stack")
	}

	// resolve to proper path
	pkgDir := filepath.Dir(callingGoFile)
	return filepath.Join(pkgDir, name), nil
}

func (b *Box) resolveAbsolutePathFromCaller() error {
	path, err := resolveAbsolutePathFromCaller(b.name, 4)
	if err != nil {
		return err
	}
	b.absolutePath = path
	return nil

}

// IsEmbedded indicates wether this box was embedded into the application
func (b *Box) IsEmbedded() bool {
	return b.embed != nil
}

// IsAppended indicates wether this box was appended to the application
func (b *Box) IsAppended() bool {
	return b.appendd != nil
}

// Time returns how actual the box is.
// When the box is embedded, it's value is saved in the embedding code.
// When the box is live, this methods returns time.Now()
func (b *Box) Time() time.Time {
	if b.IsEmbedded() {
		return b.embed.Time
	}

	//++ TODO: return time for appended box

	return time.Now()
}

// Open opens a File from the box
// If there is an error, it will be of type *os.PathError.
func (b *Box) Open(name string) (*File, error) {
	if Debug {
		fmt.Printf("Open(%s)\n", name)
	}

	if b.IsEmbedded() {
		if Debug {
			fmt.Println("Box is embedded")
		}

		// trim prefix (paths are relative to box)
		name = strings.TrimLeft(name, "/")
		if Debug {
			fmt.Printf("Trying %s\n", name)
		}

		// search for file
		ef := b.embed.Files[name]
		if ef == nil {
			if Debug {
				fmt.Println("Didn't find file in embed")
			}
			// file not found, try dir
			ed := b.embed.Dirs[name]
			if ed == nil {
				if Debug {
					fmt.Println("Didn't find dir in embed")
				}
				// dir not found, error out
				return nil, &os.PathError{
					Op:   "open",
					Path: name,
					Err:  os.ErrNotExist,
				}
			}
			if Debug {
				fmt.Println("Found dir. Returning virtual dir")
			}
			vd := newVirtualDir(ed)
			return &File{virtualD: vd}, nil
		}

		// box is embedded
		if Debug {
			fmt.Println("Found file. Returning virtual file")
		}
		vf := newVirtualFile(ef)
		return &File{virtualF: vf}, nil
	}

	if b.IsAppended() {
		// trim prefix (paths are relative to box)
		name = strings.TrimLeft(name, "/")

		// search for file
		appendedFile := b.appendd.Files[name]
		if appendedFile == nil {
			return nil, &os.PathError{
				Op:   "open",
				Path: name,
				Err:  os.ErrNotExist,
			}
		}

		// create new file
		f := &File{
			appendedF: appendedFile,
		}

		// if this file is a directory, we want to be able to read and seek
		if !appendedFile.dir {
			// looks like malformed data in zip, error now
			if appendedFile.content == nil {
				return nil, &os.PathError{
					Op:   "open",
					Path: "name",
					Err:  errors.New("error reading data from zip file"),
				}
			}
			// create new bytes.Reader
			f.appendedFileReader = bytes.NewReader(appendedFile.content)
		}

		// all done
		return f, nil
	}

	// perform os open
	if Debug {
		fmt.Printf("Using os.Open(%s)", filepath.Join(b.absolutePath, name))
	}
	file, err := os.Open(filepath.Join(b.absolutePath, name))
	if err != nil {
		return nil, err
	}
	return &File{realF: file}, nil
}

// ReadFile returns the content of the file with given name as []byte.
func (b *Box) Bytes(name string) ([]byte, error) {
	file, err := b.Open(name)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	content, err := ioutil.ReadAll(file)
	if err != nil {
		return nil, err
	}

	return content, nil
}

// MustBytes returns the content of the file with given name as []byte.
// panic's on error.
func (b *Box) MustBytes(name string) []byte {
	bts, err := b.Bytes(name)
	if err != nil {
		panic(err)
	}
	return bts
}

// String returns the content of the file with given name as string.
func (b *Box) String(name string) (string, error) {
	// check if box is embedded
	if b.IsEmbedded() {
		// find file in embed
		ef := b.embed.Files[name]
		if ef == nil {
			return "", os.ErrNotExist
		}
		// return as string
		return ef.Content, nil
	}

	// check if box is apended
	if b.IsAppended() {
		bts, err := b.Bytes(name)
		if err != nil {
			return "", err
		}
		return string(bts), nil
	}

	// open actual file from disk
	file, err := os.Open(filepath.Join(b.absolutePath, name))
	if err != nil {
		return "", err
	}
	defer file.Close()
	// read complete content
	bts, err := ioutil.ReadAll(file)
	if err != nil {
		return "", err
	}
	// return result as string
	return string(bts), nil
}

// MustString returns the content of the file with given name as string.
// panic's on error.
func (b *Box) MustString(name string) string {
	str, err := b.String(name)
	if err != nil {
		panic(err)
	}
	return str
}

// Name returns the name of the box
func (b *Box) Name() string {
	return b.name
}
<commit_msg>Let Box.String() use Bytes() for non-embedded paths. Fix commenton Bytes()<commit_after>package rice

import (
	"bytes"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"github.com/GeertJohan/go.rice/embedded"
)

// Box abstracts a directory for resources/files.
// It can either load files from disk, or from embedded code (when `rice --embed` was ran).
type Box struct {
	name         string
	absolutePath string
	embed        *embedded.EmbeddedBox
	appendd      *appendedBox
}

var defaultLocateOrder = []LocateMethod{LocateEmbedded, LocateAppended, LocateFS}

func findBox(name string, order []LocateMethod) (*Box, error) {
	b := &Box{name: name}

	// no support for absolute paths since gopath can be different on different machines.
	// therefore, required box must be located relative to package requiring it.
	if filepath.IsAbs(name) {
		return nil, errors.New("given name/path is absolute")
	}

	var err error
	for _, method := range order {
		switch method {
		case LocateEmbedded:
			if embed := embedded.EmbeddedBoxes[name]; embed != nil {
				b.embed = embed
				return b, nil
			}

		case LocateAppended:
			appendedBoxName := strings.Replace(name, `/`, `-`, -1)
			if appendd := appendedBoxes[appendedBoxName]; appendd != nil {
				b.appendd = appendd
				return b, nil
			}

		case LocateFS:
			// resolve absolute directory path
			err := b.resolveAbsolutePathFromCaller()
			if err != nil {
				continue
			}
			// check if absolutePath exists on filesystem
			info, err := os.Stat(b.absolutePath)
			if err != nil {
				continue
			}
			// check if absolutePath is actually a directory
			if !info.IsDir() {
				err = errors.New("given name/path is not a directory")
				continue
			}
			return b, nil
		}
	}

	if err == nil {
		err = fmt.Errorf("could not locate box %q", name)
	}

	return nil, err
}

// FindBox returns a Box instance for given name.
// When the given name is a relative path, it's base path will be the calling pkg/cmd's source root.
// When the given name is absolute, it's absolute. derp.
// Make sure the path doesn't contain any sensitive information as it might be placed into generated go source (embedded).
func FindBox(name string) (*Box, error) {
	return findBox(name, defaultLocateOrder)
}

// MustFindBox returns a Box instance for given name, like FindBox does.
// It does not return an error, instead it panics when an error occurs.
func MustFindBox(name string) *Box {
	box, err := findBox(name, defaultLocateOrder)
	if err != nil {
		panic(err)
	}
	return box
}

// This is injected as a mutable function literal so that we can mock it out in
// tests and return a fixed test file.
var resolveAbsolutePathFromCaller = func(name string, nStackFrames int) (string, error) {
	_, callingGoFile, _, ok := runtime.Caller(nStackFrames)
	if !ok {
		return "", errors.New("couldn't find caller on stack")
	}

	// resolve to proper path
	pkgDir := filepath.Dir(callingGoFile)
	return filepath.Join(pkgDir, name), nil
}

func (b *Box) resolveAbsolutePathFromCaller() error {
	path, err := resolveAbsolutePathFromCaller(b.name, 4)
	if err != nil {
		return err
	}
	b.absolutePath = path
	return nil

}

// IsEmbedded indicates wether this box was embedded into the application
func (b *Box) IsEmbedded() bool {
	return b.embed != nil
}

// IsAppended indicates wether this box was appended to the application
func (b *Box) IsAppended() bool {
	return b.appendd != nil
}

// Time returns how actual the box is.
// When the box is embedded, it's value is saved in the embedding code.
// When the box is live, this methods returns time.Now()
func (b *Box) Time() time.Time {
	if b.IsEmbedded() {
		return b.embed.Time
	}

	//++ TODO: return time for appended box

	return time.Now()
}

// Open opens a File from the box
// If there is an error, it will be of type *os.PathError.
func (b *Box) Open(name string) (*File, error) {
	if Debug {
		fmt.Printf("Open(%s)\n", name)
	}

	if b.IsEmbedded() {
		if Debug {
			fmt.Println("Box is embedded")
		}

		// trim prefix (paths are relative to box)
		name = strings.TrimLeft(name, "/")
		if Debug {
			fmt.Printf("Trying %s\n", name)
		}

		// search for file
		ef := b.embed.Files[name]
		if ef == nil {
			if Debug {
				fmt.Println("Didn't find file in embed")
			}
			// file not found, try dir
			ed := b.embed.Dirs[name]
			if ed == nil {
				if Debug {
					fmt.Println("Didn't find dir in embed")
				}
				// dir not found, error out
				return nil, &os.PathError{
					Op:   "open",
					Path: name,
					Err:  os.ErrNotExist,
				}
			}
			if Debug {
				fmt.Println("Found dir. Returning virtual dir")
			}
			vd := newVirtualDir(ed)
			return &File{virtualD: vd}, nil
		}

		// box is embedded
		if Debug {
			fmt.Println("Found file. Returning virtual file")
		}
		vf := newVirtualFile(ef)
		return &File{virtualF: vf}, nil
	}

	if b.IsAppended() {
		// trim prefix (paths are relative to box)
		name = strings.TrimLeft(name, "/")

		// search for file
		appendedFile := b.appendd.Files[name]
		if appendedFile == nil {
			return nil, &os.PathError{
				Op:   "open",
				Path: name,
				Err:  os.ErrNotExist,
			}
		}

		// create new file
		f := &File{
			appendedF: appendedFile,
		}

		// if this file is a directory, we want to be able to read and seek
		if !appendedFile.dir {
			// looks like malformed data in zip, error now
			if appendedFile.content == nil {
				return nil, &os.PathError{
					Op:   "open",
					Path: "name",
					Err:  errors.New("error reading data from zip file"),
				}
			}
			// create new bytes.Reader
			f.appendedFileReader = bytes.NewReader(appendedFile.content)
		}

		// all done
		return f, nil
	}

	// perform os open
	if Debug {
		fmt.Printf("Using os.Open(%s)", filepath.Join(b.absolutePath, name))
	}
	file, err := os.Open(filepath.Join(b.absolutePath, name))
	if err != nil {
		return nil, err
	}
	return &File{realF: file}, nil
}

// Bytes returns the content of the file with given name as []byte.
func (b *Box) Bytes(name string) ([]byte, error) {
	file, err := b.Open(name)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	content, err := ioutil.ReadAll(file)
	if err != nil {
		return nil, err
	}

	return content, nil
}

// MustBytes returns the content of the file with given name as []byte.
// panic's on error.
func (b *Box) MustBytes(name string) []byte {
	bts, err := b.Bytes(name)
	if err != nil {
		panic(err)
	}
	return bts
}

// String returns the content of the file with given name as string.
func (b *Box) String(name string) (string, error) {
	// check if box is embedded, optimized fast path
	if b.IsEmbedded() {
		// find file in embed
		ef := b.embed.Files[name]
		if ef == nil {
			return "", os.ErrNotExist
		}
		// return as string
		return ef.Content, nil
	}

	bts, err := b.Bytes(name)
	if err != nil {
		return "", err
	}
	return string(bts), nil
}

// MustString returns the content of the file with given name as string.
// panic's on error.
func (b *Box) MustString(name string) string {
	str, err := b.String(name)
	if err != nil {
		panic(err)
	}
	return str
}

// Name returns the name of the box
func (b *Box) Name() string {
	return b.name
}
<|endoftext|>