<commit_before>package admin

import (
	"bytes"
	"database/sql"
	"encoding/json"
	"fmt"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/jinzhu/gorm"
	"github.com/jinzhu/now"
	"github.com/qor/qor"
	"github.com/qor/qor/media_library"
	"github.com/qor/qor/resource"
	"github.com/qor/qor/roles"
	"github.com/qor/qor/utils"
)

type Meta struct {
	base          *Resource
	Name          string
	DBName        string
	Alias         string
	Label         string
	Type          string
	Valuer        func(interface{}, *qor.Context) interface{}
	Setter        func(resource interface{}, metaValue *resource.MetaValue, context *qor.Context)
	Metas         []resource.Metaor
	Resource      resource.Resourcer
	Collection    interface{}
	GetCollection func(interface{}, *qor.Context) [][]string
	Permission    *roles.Permission
}

func (meta *Meta) GetName() string {
	return meta.Name
}

func (meta *Meta) GetFieldName() string {
	return meta.Alias
}

func (meta *Meta) GetMetas() []resource.Metaor {
	if len(meta.Metas) > 0 {
		return meta.Metas
	} else if meta.Resource == nil {
		return []resource.Metaor{}
	} else {
		return meta.Resource.GetMetas([]string{})
	}
}

func (meta *Meta) GetResource() resource.Resourcer {
	return meta.Resource
}

func (meta *Meta) GetValuer() func(interface{}, *qor.Context) interface{} {
	return meta.Valuer
}

func (meta *Meta) GetSetter() func(resource interface{}, metaValue *resource.MetaValue, context *qor.Context) {
	return meta.Setter
}

func (meta *Meta) HasPermission(mode roles.PermissionMode, context *qor.Context) bool {
	if meta.Permission == nil {
		return true
	}
	return meta.Permission.HasPermission(mode, context.Roles...)
}

func getField(fields map[string]*gorm.Field, name string) (*gorm.Field, bool) {
	for _, field := range fields {
		if field.Name == name || field.DBName == name {
			return field, true
		}
	}
	return nil, false
}

func (meta *Meta) updateMeta() {
	if meta.Name == "" {
		utils.ExitWithMsg("Meta should have name: %v", reflect.ValueOf(meta).Type())
	} else if meta.Alias == "" {
		meta.Alias = meta.Name
	}

	if meta.Label == "" {
		meta.Label = utils.HumanizeString(meta.Name)
	}

	var (
		scope       = &gorm.Scope{Value: meta.base.Value}
		nestedField = strings.Contains(meta.Alias, ".")
		field       *gorm.Field
		hasColumn   bool
	)

	if nestedField {
		subModel, name := utils.ParseNestedField(reflect.ValueOf(meta.base.Value), meta.Alias)
		subScope := &gorm.Scope{Value: subModel.Interface()}
		field, hasColumn = getField(subScope.Fields(), name)
	} else {
		if field, hasColumn = getField(scope.Fields(), meta.Alias); hasColumn {
			meta.Alias = field.Name
			meta.DBName = field.DBName
		}
	}

	var fieldType reflect.Type
	if hasColumn {
		fieldType = field.Field.Type()
		for fieldType.Kind() == reflect.Ptr {
			fieldType = fieldType.Elem()
		}
	}

	// Set Meta Type
	if meta.Type == "" && hasColumn {
		if relationship := field.Relationship; relationship != nil {
			if relationship.Kind == "belongs_to" || relationship.Kind == "has_one" {
				meta.Type = "single_edit"
			} else if relationship.Kind == "has_many" {
				meta.Type = "collection_edit"
			} else if relationship.Kind == "many_to_many" {
				meta.Type = "select_many"
			}
		} else {
			switch fieldType.Kind().String() {
			case "string":
				if size, ok := utils.ParseTagOption(field.Tag.Get("sql"))["SIZE"]; ok {
					if i, _ := strconv.Atoi(size); i > 255 {
						meta.Type = "text"
					} else {
						meta.Type = "string"
					}
				} else if text, ok := utils.ParseTagOption(field.Tag.Get("sql"))["TYPE"]; ok && text == "text" {
					meta.Type = "text"
				} else {
					meta.Type = "string"
				}
			case "bool":
				meta.Type = "checkbox"
			default:
				if regexp.MustCompile(`^(.*)?(u)?(int)(\d+)?`).MatchString(fieldType.Kind().String()) {
					meta.Type = "number"
				} else if regexp.MustCompile(`^(.*)?(float)(\d+)?`).MatchString(fieldType.Kind().String()) {
					meta.Type = "float"
				} else if _, ok := reflect.New(fieldType).Interface().(*time.Time); ok {
					meta.Type = "datetime"
				} else if _, ok := reflect.New(fieldType).Interface().(media_library.MediaLibrary); ok {
					meta.Type = "file"
				}
			}
		}
	}

	// Set Meta Resource
	if meta.Resource == nil {
		if hasColumn && (field.Relationship != nil) {
			var result interface{}
			if fieldType.Kind().String() == "struct" {
				result = reflect.New(field.Field.Type()).Interface()
			} else if fieldType.Kind().String() == "slice" {
				result = reflect.New(field.Field.Type().Elem()).Interface()
			}

			res := meta.base.GetAdmin().NewResource(result)
			res.compile()
			meta.Resource = res
		}
	}

	if meta.Type == "serialize_argument" {
		type SerializeArgumentInterface interface {
			GetSerializeArgumentResource() *Resource
			GetSerializeArgument() interface{}
			SetSerializeArgument(value interface{})
		}

		if meta.Valuer == nil {
			meta.Valuer = func(value interface{}, context *qor.Context) interface{} {
				if serializeArgument, ok := value.(SerializeArgumentInterface); ok {
					return struct {
						Value    interface{}
						Resource *Resource
					}{
						Value:    serializeArgument.GetSerializeArgument(),
						Resource: serializeArgument.GetSerializeArgumentResource(),
					}
				}
				return nil
			}
		}

		if meta.Setter == nil {
			meta.Setter = func(result interface{}, metaValue *resource.MetaValue, context *qor.Context) {
				if serializeArgument, ok := result.(SerializeArgumentInterface); ok {
					if res := serializeArgument.GetSerializeArgumentResource(); res != nil {
						value := res.NewStruct()

						for _, meta := range res.GetMetas([]string{}) {
							metaValue := metaValue.MetaValues.Get(meta.GetName())
							if setter := meta.GetSetter(); setter != nil {
								setter(value, metaValue, context)
							}
						}

						serializeArgument.SetSerializeArgument(value)
					}
				}
			}
		}
	}

	// Set Meta Valuer
	if meta.Valuer == nil {
		if hasColumn {
			meta.Valuer = func(value interface{}, context *qor.Context) interface{} {
				scope := context.GetDB().NewScope(value)
				alias := meta.Alias
				if nestedField {
					fields := strings.Split(alias, ".")
					alias = fields[len(fields)-1]
				}

				if f, ok := scope.FieldByName(alias); ok {
					if field.Relationship != nil {
						if f.Field.CanAddr() && !scope.PrimaryKeyZero() {
							context.GetDB().Model(value).Related(f.Field.Addr().Interface(), meta.Alias)
						}
					}
					if f.Field.CanAddr() {
						return f.Field.Addr().Interface()
					} else {
						return f.Field.Interface()
					}
				}

				return ""
			}
		} else {
			utils.ExitWithMsg("Unsupported meta name %v for resource %v", meta.Name, reflect.TypeOf(meta.base.Value))
		}
	}

	scopeField, _ := scope.FieldByName(meta.Alias)

	// Set Meta Collection
	if meta.Collection != nil {
		if maps, ok := meta.Collection.([]string); ok {
			meta.GetCollection = func(interface{}, *qor.Context) (results [][]string) {
				for _, value := range maps {
					results = append(results, []string{value, value})
				}
				return
			}
		} else if maps, ok := meta.Collection.([][]string); ok {
			meta.GetCollection = func(interface{}, *qor.Context) [][]string {
				return maps
			}
		} else if f, ok := meta.Collection.(func(interface{}, *qor.Context) [][]string); ok {
			meta.GetCollection = f
		} else {
			utils.ExitWithMsg("Unsupported Collection format for meta %v of resource %v", meta.Name, reflect.TypeOf(meta.base.Value))
		}
	} else if meta.Type == "select_one" || meta.Type == "select_many" {
		if scopeField.Relationship != nil {
			fieldType := scopeField.StructField.Struct.Type
			if fieldType.Kind() == reflect.Slice {
				fieldType = fieldType.Elem()
			}

			meta.GetCollection = func(value interface{}, context *qor.Context) (results [][]string) {
				values := reflect.New(reflect.SliceOf(fieldType)).Interface()
				context.GetDB().Find(values)
				reflectValues := reflect.Indirect(reflect.ValueOf(values))
				for i := 0; i < reflectValues.Len(); i++ {
					scope := scope.New(reflectValues.Index(i).Interface())
					primaryKey := fmt.Sprintf("%v", scope.PrimaryKeyValue())
					results = append(results, []string{primaryKey, utils.Stringify(reflectValues.Index(i).Interface())})
				}
				return
			}
		} else {
			utils.ExitWithMsg("%v meta type %v needs Collection", meta.Name, meta.Type)
		}
	}

	if meta.Setter == nil && hasColumn {
		if relationship := field.Relationship; relationship != nil {
			if relationship.Kind == "many_to_many" {
				meta.Setter = func(resource interface{}, metaValue *resource.MetaValue, context *qor.Context) {
					scope := &gorm.Scope{Value: resource}
					field := reflect.Indirect(reflect.ValueOf(resource)).FieldByName(meta.Alias)

					if field.Kind() == reflect.Ptr && field.IsNil() {
						field.Set(utils.NewValue(field.Type()).Elem())
					}

					for field.Kind() == reflect.Ptr {
						field = field.Elem()
					}

					if primaryKeys := utils.ToArray(metaValue.Value); len(primaryKeys) > 0 {
						context.GetDB().Where(primaryKeys).Find(field.Addr().Interface())
					}

					// Replace many 2 many relations
					if !scope.PrimaryKeyZero() {
						context.GetDB().Model(resource).Association(meta.Alias).Replace(field.Interface())
					}
				}
			}
		} else {
			meta.Setter = func(resource interface{}, metaValue *resource.MetaValue, context *qor.Context) {
				if metaValue == nil {
					return
				}

				value := metaValue.Value
				alias := meta.Alias
				if nestedField {
					fields := strings.Split(alias, ".")
					alias = fields[len(fields)-1]
				}

				field := reflect.Indirect(reflect.ValueOf(resource)).FieldByName(alias)
				if field.Kind() == reflect.Ptr && field.IsNil() {
					field.Set(utils.NewValue(field.Type()).Elem())
				}

				for field.Kind() == reflect.Ptr {
					field = field.Elem()
				}

				if field.IsValid() && field.CanAddr() {
					switch field.Kind() {
					case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
						field.SetInt(utils.ToInt(value))
					case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
						field.SetUint(utils.ToUint(value))
					case reflect.Float32, reflect.Float64:
						field.SetFloat(utils.ToFloat(value))
					case reflect.Bool:
						// TODO: add test
						if utils.ToString(value) == "true" {
							field.SetBool(true)
						} else {
							field.SetBool(false)
						}
					default:
						if scanner, ok := field.Addr().Interface().(sql.Scanner); ok {
							if scanner.Scan(value) != nil {
								scanner.Scan(utils.ToString(value))
							}
						} else if reflect.TypeOf("").ConvertibleTo(field.Type()) {
							field.Set(reflect.ValueOf(utils.ToString(value)).Convert(field.Type()))
						} else if reflect.TypeOf([]string{}).ConvertibleTo(field.Type()) {
							field.Set(reflect.ValueOf(utils.ToArray(value)).Convert(field.Type()))
						} else if rvalue := reflect.ValueOf(value); reflect.TypeOf(rvalue.Type()).ConvertibleTo(field.Type()) {
							field.Set(rvalue.Convert(field.Type()))
						} else if _, ok := field.Addr().Interface().(*time.Time); ok {
							if str := utils.ToString(value); str != "" {
								if newTime, err := now.Parse(str); err == nil {
									field.Set(reflect.ValueOf(newTime))
								}
							}
						} else {
							var buf = bytes.NewBufferString("")
							json.NewEncoder(buf).Encode(value)
							if err := json.NewDecoder(strings.NewReader(buf.String())).Decode(field.Addr().Interface()); err != nil {
								utils.ExitWithMsg("Can't set value %v to %v [meta %v]", reflect.ValueOf(value).Type(), field.Type(), meta)
							}
						}
					}
				}
			}
		}
	}

	if nestedField {
		oldvalue := meta.Valuer
		meta.Valuer = func(value interface{}, context *qor.Context) interface{} {
			return oldvalue(utils.GetNestedModel(value, meta.Alias, context), context)
		}
		oldSetter := meta.Setter
		meta.Setter = func(resource interface{}, metaValue *resource.MetaValue, context *qor.Context) {
			oldSetter(utils.GetNestedModel(resource, meta.Alias, context), metaValue, context)
		}
	}
}
<commit_msg>Fix Meta Setter for associations<commit_after>package admin

import (
	"bytes"
	"database/sql"
	"encoding/json"
	"fmt"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/jinzhu/gorm"
	"github.com/jinzhu/now"
	"github.com/qor/qor"
	"github.com/qor/qor/media_library"
	"github.com/qor/qor/resource"
	"github.com/qor/qor/roles"
	"github.com/qor/qor/utils"
)

type Meta struct {
	base          *Resource
	Name          string
	DBName        string
	Alias         string
	Label         string
	Type          string
	Valuer        func(interface{}, *qor.Context) interface{}
	Setter        func(resource interface{}, metaValue *resource.MetaValue, context *qor.Context)
	Metas         []resource.Metaor
	Resource      resource.Resourcer
	Collection    interface{}
	GetCollection func(interface{}, *qor.Context) [][]string
	Permission    *roles.Permission
}

func (meta *Meta) GetName() string {
	return meta.Name
}

func (meta *Meta) GetFieldName() string {
	return meta.Alias
}

func (meta *Meta) GetMetas() []resource.Metaor {
	if len(meta.Metas) > 0 {
		return meta.Metas
	} else if meta.Resource == nil {
		return []resource.Metaor{}
	} else {
		return meta.Resource.GetMetas([]string{})
	}
}

func (meta *Meta) GetResource() resource.Resourcer {
	return meta.Resource
}

func (meta *Meta) GetValuer() func(interface{}, *qor.Context) interface{} {
	return meta.Valuer
}

func (meta *Meta) GetSetter() func(resource interface{}, metaValue *resource.MetaValue, context *qor.Context) {
	return meta.Setter
}

func (meta *Meta) HasPermission(mode roles.PermissionMode, context *qor.Context) bool {
	if meta.Permission == nil {
		return true
	}
	return meta.Permission.HasPermission(mode, context.Roles...)
}

func getField(fields map[string]*gorm.Field, name string) (*gorm.Field, bool) {
	for _, field := range fields {
		if field.Name == name || field.DBName == name {
			return field, true
		}
	}
	return nil, false
}

func (meta *Meta) updateMeta() {
	if meta.Name == "" {
		utils.ExitWithMsg("Meta should have name: %v", reflect.ValueOf(meta).Type())
	} else if meta.Alias == "" {
		meta.Alias = meta.Name
	}

	if meta.Label == "" {
		meta.Label = utils.HumanizeString(meta.Name)
	}

	var (
		scope       = &gorm.Scope{Value: meta.base.Value}
		nestedField = strings.Contains(meta.Alias, ".")
		field       *gorm.Field
		hasColumn   bool
	)

	if nestedField {
		subModel, name := utils.ParseNestedField(reflect.ValueOf(meta.base.Value), meta.Alias)
		subScope := &gorm.Scope{Value: subModel.Interface()}
		field, hasColumn = getField(subScope.Fields(), name)
	} else {
		if field, hasColumn = getField(scope.Fields(), meta.Alias); hasColumn {
			meta.Alias = field.Name
			meta.DBName = field.DBName
		}
	}

	var fieldType reflect.Type
	if hasColumn {
		fieldType = field.Field.Type()
		for fieldType.Kind() == reflect.Ptr {
			fieldType = fieldType.Elem()
		}
	}

	// Set Meta Type
	if meta.Type == "" && hasColumn {
		if relationship := field.Relationship; relationship != nil {
			if relationship.Kind == "belongs_to" || relationship.Kind == "has_one" {
				meta.Type = "single_edit"
			} else if relationship.Kind == "has_many" {
				meta.Type = "collection_edit"
			} else if relationship.Kind == "many_to_many" {
				meta.Type = "select_many"
			}
		} else {
			switch fieldType.Kind().String() {
			case "string":
				if size, ok := utils.ParseTagOption(field.Tag.Get("sql"))["SIZE"]; ok {
					if i, _ := strconv.Atoi(size); i > 255 {
						meta.Type = "text"
					} else {
						meta.Type = "string"
					}
				} else if text, ok := utils.ParseTagOption(field.Tag.Get("sql"))["TYPE"]; ok && text == "text" {
					meta.Type = "text"
				} else {
					meta.Type = "string"
				}
			case "bool":
				meta.Type = "checkbox"
			default:
				if regexp.MustCompile(`^(.*)?(u)?(int)(\d+)?`).MatchString(fieldType.Kind().String()) {
					meta.Type = "number"
				} else if regexp.MustCompile(`^(.*)?(float)(\d+)?`).MatchString(fieldType.Kind().String()) {
					meta.Type = "float"
				} else if _, ok := reflect.New(fieldType).Interface().(*time.Time); ok {
					meta.Type = "datetime"
				} else if _, ok := reflect.New(fieldType).Interface().(media_library.MediaLibrary); ok {
					meta.Type = "file"
				}
			}
		}
	}

	// Set Meta Resource
	if meta.Resource == nil {
		if hasColumn && (field.Relationship != nil) {
			var result interface{}
			if fieldType.Kind().String() == "struct" {
				result = reflect.New(field.Field.Type()).Interface()
			} else if fieldType.Kind().String() == "slice" {
				result = reflect.New(field.Field.Type().Elem()).Interface()
			}

			res := meta.base.GetAdmin().NewResource(result)
			res.compile()
			meta.Resource = res
		}
	}

	if meta.Type == "serialize_argument" {
		type SerializeArgumentInterface interface {
			GetSerializeArgumentResource() *Resource
			GetSerializeArgument() interface{}
			SetSerializeArgument(value interface{})
		}

		if meta.Valuer == nil {
			meta.Valuer = func(value interface{}, context *qor.Context) interface{} {
				if serializeArgument, ok := value.(SerializeArgumentInterface); ok {
					return struct {
						Value    interface{}
						Resource *Resource
					}{
						Value:    serializeArgument.GetSerializeArgument(),
						Resource: serializeArgument.GetSerializeArgumentResource(),
					}
				}
				return nil
			}
		}

		if meta.Setter == nil {
			meta.Setter = func(result interface{}, metaValue *resource.MetaValue, context *qor.Context) {
				if serializeArgument, ok := result.(SerializeArgumentInterface); ok {
					if res := serializeArgument.GetSerializeArgumentResource(); res != nil {
						value := res.NewStruct()

						for _, meta := range res.GetMetas([]string{}) {
							metaValue := metaValue.MetaValues.Get(meta.GetName())
							if setter := meta.GetSetter(); setter != nil {
								setter(value, metaValue, context)
							}
						}

						serializeArgument.SetSerializeArgument(value)
					}
				}
			}
		}
	}

	// Set Meta Valuer
	if meta.Valuer == nil {
		if hasColumn {
			meta.Valuer = func(value interface{}, context *qor.Context) interface{} {
				scope := context.GetDB().NewScope(value)
				alias := meta.Alias
				if nestedField {
					fields := strings.Split(alias, ".")
					alias = fields[len(fields)-1]
				}

				if f, ok := scope.FieldByName(alias); ok {
					if field.Relationship != nil {
						if f.Field.CanAddr() && !scope.PrimaryKeyZero() {
							context.GetDB().Model(value).Related(f.Field.Addr().Interface(), meta.Alias)
						}
					}
					if f.Field.CanAddr() {
						return f.Field.Addr().Interface()
					} else {
						return f.Field.Interface()
					}
				}

				return ""
			}
		} else {
			utils.ExitWithMsg("Unsupported meta name %v for resource %v", meta.Name, reflect.TypeOf(meta.base.Value))
		}
	}

	scopeField, _ := scope.FieldByName(meta.Alias)

	// Set Meta Collection
	if meta.Collection != nil {
		if maps, ok := meta.Collection.([]string); ok {
			meta.GetCollection = func(interface{}, *qor.Context) (results [][]string) {
				for _, value := range maps {
					results = append(results, []string{value, value})
				}
				return
			}
		} else if maps, ok := meta.Collection.([][]string); ok {
			meta.GetCollection = func(interface{}, *qor.Context) [][]string {
				return maps
			}
		} else if f, ok := meta.Collection.(func(interface{}, *qor.Context) [][]string); ok {
			meta.GetCollection = f
		} else {
			utils.ExitWithMsg("Unsupported Collection format for meta %v of resource %v", meta.Name, reflect.TypeOf(meta.base.Value))
		}
	} else if meta.Type == "select_one" || meta.Type == "select_many" {
		if scopeField.Relationship != nil {
			fieldType := scopeField.StructField.Struct.Type
			if fieldType.Kind() == reflect.Slice {
				fieldType = fieldType.Elem()
			}

			meta.GetCollection = func(value interface{}, context *qor.Context) (results [][]string) {
				values := reflect.New(reflect.SliceOf(fieldType)).Interface()
				context.GetDB().Find(values)
				reflectValues := reflect.Indirect(reflect.ValueOf(values))
				for i := 0; i < reflectValues.Len(); i++ {
					scope := scope.New(reflectValues.Index(i).Interface())
					primaryKey := fmt.Sprintf("%v", scope.PrimaryKeyValue())
					results = append(results, []string{primaryKey, utils.Stringify(reflectValues.Index(i).Interface())})
				}
				return
			}
		} else {
			utils.ExitWithMsg("%v meta type %v needs Collection", meta.Name, meta.Type)
		}
	}

	if meta.Setter == nil && hasColumn {
		if relationship := field.Relationship; relationship != nil {
			meta.Setter = func(resource interface{}, metaValue *resource.MetaValue, context *qor.Context) {
				scope := &gorm.Scope{Value: resource}
				field := reflect.Indirect(reflect.ValueOf(resource)).FieldByName(meta.Alias)

				if field.Kind() == reflect.Ptr && field.IsNil() {
					field.Set(utils.NewValue(field.Type()).Elem())
				}

				for field.Kind() == reflect.Ptr {
					field = field.Elem()
				}

				if primaryKeys := utils.ToArray(metaValue.Value); len(primaryKeys) > 0 {
					context.GetDB().Where(primaryKeys).Find(field.Addr().Interface())
				}

				if relationship.Kind == "many_to_many" {
					// Replace many 2 many relations
					if !scope.PrimaryKeyZero() {
						context.GetDB().Model(resource).Association(meta.Alias).Replace(field.Interface())
					}
				}
			}
		} else {
			meta.Setter = func(resource interface{}, metaValue *resource.MetaValue, context *qor.Context) {
				if metaValue == nil {
					return
				}

				value := metaValue.Value
				alias := meta.Alias
				if nestedField {
					fields := strings.Split(alias, ".")
					alias = fields[len(fields)-1]
				}

				field := reflect.Indirect(reflect.ValueOf(resource)).FieldByName(alias)
				if field.Kind() == reflect.Ptr && field.IsNil() {
					field.Set(utils.NewValue(field.Type()).Elem())
				}

				for field.Kind() == reflect.Ptr {
					field = field.Elem()
				}

				if field.IsValid() && field.CanAddr() {
					switch field.Kind() {
					case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
						field.SetInt(utils.ToInt(value))
					case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
						field.SetUint(utils.ToUint(value))
					case reflect.Float32, reflect.Float64:
						field.SetFloat(utils.ToFloat(value))
					case reflect.Bool:
						// TODO: add test
						if utils.ToString(value) == "true" {
							field.SetBool(true)
						} else {
							field.SetBool(false)
						}
					default:
						if scanner, ok := field.Addr().Interface().(sql.Scanner); ok {
							if scanner.Scan(value) != nil {
								scanner.Scan(utils.ToString(value))
							}
						} else if reflect.TypeOf("").ConvertibleTo(field.Type()) {
							field.Set(reflect.ValueOf(utils.ToString(value)).Convert(field.Type()))
						} else if reflect.TypeOf([]string{}).ConvertibleTo(field.Type()) {
							field.Set(reflect.ValueOf(utils.ToArray(value)).Convert(field.Type()))
						} else if rvalue := reflect.ValueOf(value); reflect.TypeOf(rvalue.Type()).ConvertibleTo(field.Type()) {
							field.Set(rvalue.Convert(field.Type()))
						} else if _, ok := field.Addr().Interface().(*time.Time); ok {
							if str := utils.ToString(value); str != "" {
								if newTime, err := now.Parse(str); err == nil {
									field.Set(reflect.ValueOf(newTime))
								}
							}
						} else {
							var buf = bytes.NewBufferString("")
							json.NewEncoder(buf).Encode(value)
							if err := json.NewDecoder(strings.NewReader(buf.String())).Decode(field.Addr().Interface()); err != nil {
								utils.ExitWithMsg("Can't set value %v to %v [meta %v]", reflect.ValueOf(value).Type(), field.Type(), meta)
							}
						}
					}
				}
			}
		}
	}

	if nestedField {
		oldvalue := meta.Valuer
		meta.Valuer = func(value interface{}, context *qor.Context) interface{} {
			return oldvalue(utils.GetNestedModel(value, meta.Alias, context), context)
		}
		oldSetter := meta.Setter
		meta.Setter = func(resource interface{}, metaValue *resource.MetaValue, context *qor.Context) {
			oldSetter(utils.GetNestedModel(resource, meta.Alias, context), metaValue, context)
		}
	}
}
<|endoftext|>


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

// Package text offers functions to draw texts on an Ebiten's image.
//
// For the example using a TTF font, see font package in the examples.
package text

import (
	"image"
	"image/color"
	"math"
	"reflect"

	"golang.org/x/image/font"
	"golang.org/x/image/math/fixed"

	"github.com/hajimehoshi/ebiten"
	emath "github.com/hajimehoshi/ebiten/internal/math"
	"github.com/hajimehoshi/ebiten/internal/sync"
)

var (
	monotonicClock int64
)

func now() int64 {
	monotonicClock++
	return monotonicClock
}

var (
	faces = map[font.Face]struct{}{}
)

func fontFaceToFace(f font.Face) font.Face {
	if _, ok := faces[f]; ok {
		return f
	}
	// If the (DeepEqual-ly) same font exists,
	// reuse this to avoid to consume a lot of cache (#498).
	for key := range faces {
		if reflect.DeepEqual(key, f) {
			return key
		}
	}
	faces[f] = struct{}{}
	return f
}

var (
	// Use pointers to avoid copying on browsers.
	charBounds = map[font.Face]map[rune]*fixed.Rectangle26_6{}
)

type char struct {
	face   font.Face
	rune   rune
	atlasG int
}

func (c *char) bounds() *fixed.Rectangle26_6 {
	if m, ok := charBounds[c.face]; ok {
		if b, ok := m[c.rune]; ok {
			return b
		}
	} else {
		charBounds[c.face] = map[rune]*fixed.Rectangle26_6{}
	}
	b, _, _ := c.face.GlyphBounds(c.rune)
	charBounds[c.face][c.rune] = &b
	return &b
}

func (c *char) size() (fixed.Int26_6, fixed.Int26_6) {
	b := c.bounds()
	return b.Max.X - b.Min.X, b.Max.Y - b.Min.Y
}

func (c *char) empty() bool {
	x, y := c.size()
	return x == 0 || y == 0
}

func (c *char) atlasGroup() int {
	if c.atlasG != 0 {
		return c.atlasG
	}

	x, y := c.size()
	w, h := x.Ceil(), y.Ceil()
	t := w
	if t < h {
		t = h
	}

	// Different images for small runes are inefficient.
	// Let's use a same texture atlas for typical character sizes.
	if t < 32 {
		return 32
	}
	c.atlasG = emath.NextPowerOf2Int(t)
	return c.atlasG
}

type glyph struct {
	char  char
	index int
	atime int64
}

func fixed26_6ToFloat64(x fixed.Int26_6) float64 {
	return float64(x) / (1 << 6)
}

type colorMCacheEntry struct {
	m     ebiten.ColorM
	atime int64
}

var (
	colorMCache = map[color.Color]*colorMCacheEntry{}
)

func (g *glyph) draw(dst *ebiten.Image, x, y fixed.Int26_6, clr color.Color) {
	cr, cg, cb, ca := clr.RGBA()
	if ca == 0 {
		return
	}

	b := g.char.bounds()
	op := &ebiten.DrawImageOptions{}
	op.GeoM.Translate(fixed26_6ToFloat64(x+b.Min.X), fixed26_6ToFloat64(y+b.Min.Y))

	e, ok := colorMCache[clr]
	if ok {
		e.atime = now()
	} else {
		if len(colorMCache) >= 256 {
			var oldest color.Color
			t := int64(math.MaxInt64)
			for key, e := range colorMCache {
				if e.atime < t {
					t = g.atime
					oldest = key
				}
			}
			delete(colorMCache, oldest)
		}
		cm := ebiten.ColorM{}
		rf := float64(cr) / float64(ca)
		gf := float64(cg) / float64(ca)
		bf := float64(cb) / float64(ca)
		af := float64(ca) / 0xffff
		cm.Scale(rf, gf, bf, af)
		e = &colorMCacheEntry{
			m:     cm,
			atime: now(),
		}
		colorMCache[clr] = e
	}
	op.ColorM = e.m

	a := atlases[g.char.face][g.char.atlasGroup()]
	sx, sy := a.at(g)
	r := image.Rect(sx, sy, sx+a.glyphSize, sy+a.glyphSize)
	op.SourceRect = &r

	dst.DrawImage(a.image, op)
}

var (
	atlases = map[font.Face]map[int]*atlas{}
)

type atlas struct {
	// image is the back-end image to hold glyph cache.
	image *ebiten.Image

	// tmpImage is the temporary image as a renderer source for glyph.
	tmpImage *ebiten.Image

	// glyphSize is the size of one glyph in the cache.
	// This value is always power of 2.
	glyphSize int

	runeToGlyph map[rune]*glyph
}

func (a *atlas) at(glyph *glyph) (int, int) {
	if a.glyphSize != glyph.char.atlasGroup() {
		panic("not reached")
	}
	w, _ := a.image.Size()
	xnum := w / a.glyphSize
	x, y := glyph.index%xnum, glyph.index/xnum
	return x * a.glyphSize, y * a.glyphSize
}

func (a *atlas) maxGlyphNum() int {
	w, h := a.image.Size()
	xnum := w / a.glyphSize
	ynum := h / a.glyphSize
	return xnum * ynum
}

func (a *atlas) appendGlyph(char char, now int64) *glyph {
	g := &glyph{
		char:  char,
		atime: now,
	}
	if len(a.runeToGlyph) == a.maxGlyphNum() {
		var oldest *glyph
		t := int64(math.MaxInt64)
		for _, g := range a.runeToGlyph {
			if g.atime < t {
				t = g.atime
				oldest = g
			}
		}
		if oldest == nil {
			panic("not reached")
		}
		idx := oldest.index
		delete(a.runeToGlyph, oldest.char.rune)

		g.index = idx
	} else {
		g.index = len(a.runeToGlyph)
	}
	a.runeToGlyph[g.char.rune] = g
	a.draw(g)
	return g
}

func (a *atlas) draw(glyph *glyph) {
	if a.tmpImage == nil {
		a.tmpImage, _ = ebiten.NewImage(a.glyphSize, a.glyphSize, ebiten.FilterNearest)
	}

	dst := image.NewRGBA(image.Rect(0, 0, a.glyphSize, a.glyphSize))
	d := font.Drawer{
		Dst:  dst,
		Src:  image.White,
		Face: glyph.char.face,
	}
	b := glyph.char.bounds()
	d.Dot = fixed.Point26_6{-b.Min.X, -b.Min.Y}
	d.DrawString(string(glyph.char.rune))
	a.tmpImage.ReplacePixels(dst.Pix)

	op := &ebiten.DrawImageOptions{}
	x, y := a.at(glyph)
	op.GeoM.Translate(float64(x), float64(y))
	op.CompositeMode = ebiten.CompositeModeCopy
	a.image.DrawImage(a.tmpImage, op)

	a.tmpImage.Clear()
}

func getGlyphFromCache(face font.Face, r rune, now int64) *glyph {
	ch := char{
		face: face,
		rune: r,
	}
	var at *atlas
	if m, ok := atlases[face]; ok {
		a, ok := m[ch.atlasGroup()]
		if ok {
			g, ok := a.runeToGlyph[r]
			if ok {
				g.atime = now
				return g
			}
		}
		at = a
	} else {
		atlases[face] = map[int]*atlas{}
	}

	if ch.empty() {
		// The glyph doesn't have its size but might have valid 'advance' parameter
		// when ch is e.g. space (U+0020).
		return &glyph{
			char:  ch,
			atime: now,
		}
	}

	if at == nil {
		// Don't use ebiten.MaxImageSize here.
		// It's because the back-end image pixels will be restored from GPU
		// whenever a new glyph is rendered on the image, and restoring cost is
		// expensive if the image is big.
		// The back-end image is updated a temporary image, and the temporary image is
		// always cleared after used. This means that there is no clue to restore
		// the back-end image without reading from GPU
		// (see the package 'restorable' implementation).
		//
		// TODO: How about making a new function for 'flagile' image?
		const size = 1024
		i, _ := ebiten.NewImage(size, size, ebiten.FilterNearest)
		at = &atlas{
			image:       i,
			glyphSize:   ch.atlasGroup(),
			runeToGlyph: map[rune]*glyph{},
		}
		atlases[face][ch.atlasGroup()] = at
	}

	return at.appendGlyph(ch, now)
}

var textM sync.Mutex

// Draw draws a given text on a given destination image dst.
//
// face is the font for text rendering.
// (x, y) represents a 'dot' (period) position.
// Be careful that this doesn't represent left-upper corner position.
// clr is the color for text rendering.
//
// Glyphs used for rendering are cached in least-recently-used way.
// It is OK to call this function with a same text and a same face at every frame in terms of performance.
//
// This function is concurrent-safe.
func Draw(dst *ebiten.Image, text string, face font.Face, x, y int, clr color.Color) {
	textM.Lock()

	n := now()
	fx := fixed.I(x)
	prevC := rune(-1)

	runes := []rune(text)
	for _, c := range runes {
		if prevC >= 0 {
			fx += face.Kern(prevC, c)
		}
		fa := fontFaceToFace(face)
		if g := getGlyphFromCache(fa, c, n); g != nil {
			if !g.char.empty() {
				g.draw(dst, fx, fixed.I(y), clr)
			}
			a, _ := face.GlyphAdvance(c)
			fx += a
		}
		prevC = c
	}

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

// Package text offers functions to draw texts on an Ebiten's image.
//
// For the example using a TTF font, see font package in the examples.
package text

import (
	"image"
	"image/color"
	"math"
	"reflect"

	"golang.org/x/image/font"
	"golang.org/x/image/math/fixed"

	"github.com/hajimehoshi/ebiten"
	emath "github.com/hajimehoshi/ebiten/internal/math"
	"github.com/hajimehoshi/ebiten/internal/sync"
)

var (
	monotonicClock int64
)

func now() int64 {
	monotonicClock++
	return monotonicClock
}

var (
	faces = map[font.Face]struct{}{}
)

func fontFaceToFace(f font.Face) font.Face {
	if _, ok := faces[f]; ok {
		return f
	}
	// If the (DeepEqual-ly) same font exists,
	// reuse this to avoid to consume a lot of cache (#498).
	for key := range faces {
		if reflect.DeepEqual(key, f) {
			return key
		}
	}
	faces[f] = struct{}{}
	return f
}

var (
	// Use pointers to avoid copying on browsers.
	charBounds = map[font.Face]map[rune]*fixed.Rectangle26_6{}
)

type char struct {
	face   font.Face
	rune   rune
	atlasG int
}

func (c *char) bounds() *fixed.Rectangle26_6 {
	if m, ok := charBounds[c.face]; ok {
		if b, ok := m[c.rune]; ok {
			return b
		}
	} else {
		charBounds[c.face] = map[rune]*fixed.Rectangle26_6{}
	}
	b, _, _ := c.face.GlyphBounds(c.rune)
	charBounds[c.face][c.rune] = &b
	return &b
}

func (c *char) size() (fixed.Int26_6, fixed.Int26_6) {
	b := c.bounds()
	return b.Max.X - b.Min.X, b.Max.Y - b.Min.Y
}

func (c *char) empty() bool {
	x, y := c.size()
	return x == 0 || y == 0
}

func (c *char) atlasGroup() int {
	if c.atlasG != 0 {
		return c.atlasG
	}

	x, y := c.size()
	w, h := x.Ceil(), y.Ceil()
	t := w
	if t < h {
		t = h
	}

	// Different images for small runes are inefficient.
	// Let's use a same texture atlas for typical character sizes.
	if t < 32 {
		return 32
	}
	c.atlasG = emath.NextPowerOf2Int(t)
	return c.atlasG
}

type glyph struct {
	char  char
	index int
	atime int64
}

func fixed26_6ToFloat64(x fixed.Int26_6) float64 {
	return float64(x) / (1 << 6)
}

type colorMCacheKey uint32

type colorMCacheEntry struct {
	m     ebiten.ColorM
	atime int64
}

var (
	colorMCache = map[colorMCacheKey]*colorMCacheEntry{}
)

func (g *glyph) draw(dst *ebiten.Image, x, y fixed.Int26_6, clr color.Color) {
	// RGBA() is in [0 - 0xffff]. Adjust them in [0 - 0xff].
	cr, cg, cb, ca := clr.RGBA()
	cr >>= 8
	cg >>= 8
	cb >>= 8
	ca >>= 8
	if ca == 0 {
		return
	}

	b := g.char.bounds()
	op := &ebiten.DrawImageOptions{}
	op.GeoM.Translate(fixed26_6ToFloat64(x+b.Min.X), fixed26_6ToFloat64(y+b.Min.Y))

	key := colorMCacheKey(uint32(cr) | (uint32(cg) << 8) | (uint32(cb) << 16) | (uint32(ca) << 24))
	e, ok := colorMCache[key]
	if ok {
		e.atime = now()
	} else {
		if len(colorMCache) >= 256 {
			var oldest colorMCacheKey
			t := int64(math.MaxInt64)
			for key, e := range colorMCache {
				if e.atime < t {
					t = g.atime
					oldest = key
				}
			}
			delete(colorMCache, oldest)
		}
		cm := ebiten.ColorM{}
		rf := float64(cr) / float64(ca)
		gf := float64(cg) / float64(ca)
		bf := float64(cb) / float64(ca)
		af := float64(ca) / 0xff
		cm.Scale(rf, gf, bf, af)
		e = &colorMCacheEntry{
			m:     cm,
			atime: now(),
		}
		colorMCache[key] = e
	}
	op.ColorM = e.m

	a := atlases[g.char.face][g.char.atlasGroup()]
	sx, sy := a.at(g)
	r := image.Rect(sx, sy, sx+a.glyphSize, sy+a.glyphSize)
	op.SourceRect = &r

	dst.DrawImage(a.image, op)
}

var (
	atlases = map[font.Face]map[int]*atlas{}
)

type atlas struct {
	// image is the back-end image to hold glyph cache.
	image *ebiten.Image

	// tmpImage is the temporary image as a renderer source for glyph.
	tmpImage *ebiten.Image

	// glyphSize is the size of one glyph in the cache.
	// This value is always power of 2.
	glyphSize int

	runeToGlyph map[rune]*glyph
}

func (a *atlas) at(glyph *glyph) (int, int) {
	if a.glyphSize != glyph.char.atlasGroup() {
		panic("not reached")
	}
	w, _ := a.image.Size()
	xnum := w / a.glyphSize
	x, y := glyph.index%xnum, glyph.index/xnum
	return x * a.glyphSize, y * a.glyphSize
}

func (a *atlas) maxGlyphNum() int {
	w, h := a.image.Size()
	xnum := w / a.glyphSize
	ynum := h / a.glyphSize
	return xnum * ynum
}

func (a *atlas) appendGlyph(char char, now int64) *glyph {
	g := &glyph{
		char:  char,
		atime: now,
	}
	if len(a.runeToGlyph) == a.maxGlyphNum() {
		var oldest *glyph
		t := int64(math.MaxInt64)
		for _, g := range a.runeToGlyph {
			if g.atime < t {
				t = g.atime
				oldest = g
			}
		}
		if oldest == nil {
			panic("not reached")
		}
		idx := oldest.index
		delete(a.runeToGlyph, oldest.char.rune)

		g.index = idx
	} else {
		g.index = len(a.runeToGlyph)
	}
	a.runeToGlyph[g.char.rune] = g
	a.draw(g)
	return g
}

func (a *atlas) draw(glyph *glyph) {
	if a.tmpImage == nil {
		a.tmpImage, _ = ebiten.NewImage(a.glyphSize, a.glyphSize, ebiten.FilterNearest)
	}

	dst := image.NewRGBA(image.Rect(0, 0, a.glyphSize, a.glyphSize))
	d := font.Drawer{
		Dst:  dst,
		Src:  image.White,
		Face: glyph.char.face,
	}
	b := glyph.char.bounds()
	d.Dot = fixed.Point26_6{-b.Min.X, -b.Min.Y}
	d.DrawString(string(glyph.char.rune))
	a.tmpImage.ReplacePixels(dst.Pix)

	op := &ebiten.DrawImageOptions{}
	x, y := a.at(glyph)
	op.GeoM.Translate(float64(x), float64(y))
	op.CompositeMode = ebiten.CompositeModeCopy
	a.image.DrawImage(a.tmpImage, op)

	a.tmpImage.Clear()
}

func getGlyphFromCache(face font.Face, r rune, now int64) *glyph {
	ch := char{
		face: face,
		rune: r,
	}
	var at *atlas
	if m, ok := atlases[face]; ok {
		a, ok := m[ch.atlasGroup()]
		if ok {
			g, ok := a.runeToGlyph[r]
			if ok {
				g.atime = now
				return g
			}
		}
		at = a
	} else {
		atlases[face] = map[int]*atlas{}
	}

	if ch.empty() {
		// The glyph doesn't have its size but might have valid 'advance' parameter
		// when ch is e.g. space (U+0020).
		return &glyph{
			char:  ch,
			atime: now,
		}
	}

	if at == nil {
		// Don't use ebiten.MaxImageSize here.
		// It's because the back-end image pixels will be restored from GPU
		// whenever a new glyph is rendered on the image, and restoring cost is
		// expensive if the image is big.
		// The back-end image is updated a temporary image, and the temporary image is
		// always cleared after used. This means that there is no clue to restore
		// the back-end image without reading from GPU
		// (see the package 'restorable' implementation).
		//
		// TODO: How about making a new function for 'flagile' image?
		const size = 1024
		i, _ := ebiten.NewImage(size, size, ebiten.FilterNearest)
		at = &atlas{
			image:       i,
			glyphSize:   ch.atlasGroup(),
			runeToGlyph: map[rune]*glyph{},
		}
		atlases[face][ch.atlasGroup()] = at
	}

	return at.appendGlyph(ch, now)
}

var textM sync.Mutex

// Draw draws a given text on a given destination image dst.
//
// face is the font for text rendering.
// (x, y) represents a 'dot' (period) position.
// Be careful that this doesn't represent left-upper corner position.
// clr is the color for text rendering.
//
// Glyphs used for rendering are cached in least-recently-used way.
// It is OK to call this function with a same text and a same face at every frame in terms of performance.
//
// This function is concurrent-safe.
func Draw(dst *ebiten.Image, text string, face font.Face, x, y int, clr color.Color) {
	textM.Lock()

	n := now()
	fx := fixed.I(x)
	prevC := rune(-1)

	runes := []rune(text)
	for _, c := range runes {
		if prevC >= 0 {
			fx += face.Kern(prevC, c)
		}
		fa := fontFaceToFace(face)
		if g := getGlyphFromCache(fa, c, n); g != nil {
			if !g.char.empty() {
				g.draw(dst, fx, fixed.I(y), clr)
			}
			a, _ := face.GlyphAdvance(c)
			fx += a
		}
		prevC = c
	}

	textM.Unlock()
}
<|endoftext|>


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

// Copyright 2015 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

package expression

import (
	"fmt"
	"strings"

	"github.com/juju/errors"
	"github.com/pingcap/tidb/context"
	"github.com/pingcap/tidb/expression/builtin"
)

var (
	_ Expression = (*Call)(nil)
)

// Call is for function expression.
type Call struct {
	// F is the function name.
	F string
	// Args is the function args.
	Args []Expression
	// Distinct only affetcts sum, avg, count, group_concat,
	// so we can ignore it in other functions
	Distinct bool

	d *builtin.AggregateDistinct
}

// NewCall creates a Call expression with function name f, function args arg and
// a distinct flag whether this function supports distinct or not.
func NewCall(f string, args []Expression, distinct bool) (v Expression, err error) {
	x := builtin.Funcs[strings.ToLower(f)]
	if x.F == nil {
		return nil, errors.Errorf("undefined: %s", f)
	}

	if g, min, max := len(args), x.MinArgs, x.MaxArgs; g < min || (max != -1 && g > max) {
		a := []interface{}{}
		for _, v := range args {
			a = append(a, v)
		}
		return nil, badNArgs(min, f, a)
	}

	c := Call{F: f, Distinct: distinct}
	for _, Val := range args {
		if !Val.IsStatic() {
			c.Args = append(c.Args, Val)
			continue
		}

		eVal, err := Val.Eval(nil, nil)
		if err != nil {
			return nil, err
		}

		c.Args = append(c.Args, Value{eVal})
	}

	return &c, nil
}

// Clone implements the Expression Clone interface.
func (c *Call) Clone() Expression {
	list := cloneExpressionList(c.Args)
	return &Call{F: c.F, Args: list, Distinct: c.Distinct}
}

// IsStatic implements the Expression IsStatic interface.
func (c *Call) IsStatic() bool {
	v := builtin.Funcs[strings.ToLower(c.F)]
	if v.F == nil || !v.IsStatic {
		return false
	}

	for _, v := range c.Args {
		if !v.IsStatic() {
			return false
		}
	}
	return true
}

// String implements the Expression String interface.
func (c *Call) String() string {
	distinct := ""
	if c.Distinct {
		distinct = "DISTINCT "
	}
	a := []string{}
	for _, v := range c.Args {
		a = append(a, v.String())
	}
	return fmt.Sprintf("%s(%s%s)", c.F, distinct, strings.Join(a, ", "))
}

// Eval implements the Expression Eval interface.
func (c *Call) Eval(ctx context.Context, args map[interface{}]interface{}) (v interface{}, err error) {
	f, ok := builtin.Funcs[strings.ToLower(c.F)]
	if !ok {
		return nil, errors.Errorf("unknown function %s", c.F)
	}

	a := make([]interface{}, len(c.Args))
	for i, arg := range c.Args {
		if v, err = arg.Eval(ctx, args); err != nil {
			return nil, err
		}
		a[i] = v
	}

	c.createDistinct()

	if args != nil {
		args[builtin.ExprEvalFn] = c
		args[builtin.ExprEvalArgCtx] = ctx
		args[builtin.ExprAggDistinct] = c.d
	}
	return f.F(a, args)
}

// Accept implements Expression Accept interface.
func (c *Call) Accept(v Visitor) (Expression, error) {
	return v.VisitCall(c)
}

func (c *Call) createDistinct() {
	if c.d != nil {
		return
	}

	c.d = builtin.CreateAggregateDistinct(c.F, c.Distinct)
}

func badNArgs(min int, s string, args []interface{}) error {
	a := []string{}
	for _, v := range args {
		a = append(a, fmt.Sprintf("%v", v))
	}
	switch len(args) < min {
	case true:
		return errors.Errorf("missing argument to %s(%s)", s, strings.Join(a, ", "))
	default: //case false:
		return errors.Errorf("too many arguments to %s(%s)", s, strings.Join(a, ", "))
	}
}
<commit_msg>expression: cleanup creating distinct in call<commit_after>// Copyright 2013 The ql Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSES/QL-LICENSE file.

// Copyright 2015 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

package expression

import (
	"fmt"
	"strings"

	"github.com/juju/errors"
	"github.com/pingcap/tidb/context"
	"github.com/pingcap/tidb/expression/builtin"
)

var (
	_ Expression = (*Call)(nil)
)

// Call is for function expression.
type Call struct {
	// F is the function name.
	F string
	// Args is the function args.
	Args []Expression
	// Distinct only affetcts sum, avg, count, group_concat,
	// so we can ignore it in other functions
	Distinct bool

	d *builtin.AggregateDistinct
}

// NewCall creates a Call expression with function name f, function args arg and
// a distinct flag whether this function supports distinct or not.
func NewCall(f string, args []Expression, distinct bool) (v Expression, err error) {
	x := builtin.Funcs[strings.ToLower(f)]
	if x.F == nil {
		return nil, errors.Errorf("undefined: %s", f)
	}

	if g, min, max := len(args), x.MinArgs, x.MaxArgs; g < min || (max != -1 && g > max) {
		a := []interface{}{}
		for _, v := range args {
			a = append(a, v)
		}
		return nil, badNArgs(min, f, a)
	}

	c := Call{F: f, Distinct: distinct}
	for _, Val := range args {
		if !Val.IsStatic() {
			c.Args = append(c.Args, Val)
			continue
		}

		eVal, err := Val.Eval(nil, nil)
		if err != nil {
			return nil, err
		}

		c.Args = append(c.Args, Value{eVal})
	}

	return &c, nil
}

// Clone implements the Expression Clone interface.
func (c *Call) Clone() Expression {
	list := cloneExpressionList(c.Args)
	return &Call{F: c.F, Args: list, Distinct: c.Distinct}
}

// IsStatic implements the Expression IsStatic interface.
func (c *Call) IsStatic() bool {
	v := builtin.Funcs[strings.ToLower(c.F)]
	if v.F == nil || !v.IsStatic {
		return false
	}

	for _, v := range c.Args {
		if !v.IsStatic() {
			return false
		}
	}
	return true
}

// String implements the Expression String interface.
func (c *Call) String() string {
	distinct := ""
	if c.Distinct {
		distinct = "DISTINCT "
	}
	a := []string{}
	for _, v := range c.Args {
		a = append(a, v.String())
	}
	return fmt.Sprintf("%s(%s%s)", c.F, distinct, strings.Join(a, ", "))
}

// Eval implements the Expression Eval interface.
func (c *Call) Eval(ctx context.Context, args map[interface{}]interface{}) (v interface{}, err error) {
	f, ok := builtin.Funcs[strings.ToLower(c.F)]
	if !ok {
		return nil, errors.Errorf("unknown function %s", c.F)
	}

	a := make([]interface{}, len(c.Args))
	for i, arg := range c.Args {
		if v, err = arg.Eval(ctx, args); err != nil {
			return nil, err
		}
		a[i] = v
	}

	if c.d == nil {
		// create a distinct if nil.
		c.d = builtin.CreateAggregateDistinct(c.F, c.Distinct)
	}

	if args != nil {
		args[builtin.ExprEvalFn] = c
		args[builtin.ExprEvalArgCtx] = ctx
		args[builtin.ExprAggDistinct] = c.d
	}
	return f.F(a, args)
}

// Accept implements Expression Accept interface.
func (c *Call) Accept(v Visitor) (Expression, error) {
	return v.VisitCall(c)
}

func badNArgs(min int, s string, args []interface{}) error {
	a := []string{}
	for _, v := range args {
		a = append(a, fmt.Sprintf("%v", v))
	}
	switch len(args) < min {
	case true:
		return errors.Errorf("missing argument to %s(%s)", s, strings.Join(a, ", "))
	default: //case false:
		return errors.Errorf("too many arguments to %s(%s)", s, strings.Join(a, ", "))
	}
}
<|endoftext|>


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

// Binary dnsmasq_exporter is a Prometheus exporter for dnsmasq statistics.
package main

import (
	"bufio"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"strconv"

	"golang.org/x/sync/errgroup"

	"github.com/miekg/dns"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

var (
	listen = flag.String("listen",
		"localhost:9153",
		"listen address")

	leasesPath = flag.String("leases_path",
		"/var/lib/misc/dnsmasq.leases",
		"path to the dnsmasq leases file")
)

var (
	// floatMetrics contains prometheus Gauges, keyed by the stats DNS record
	// they correspond to.
	floatMetrics = map[string]prometheus.Gauge{
		"cachesize.bind.": prometheus.NewGauge(prometheus.GaugeOpts{
			Name: "dnsmasq_cachesize",
			Help: "configured size of the DNS cache",
		}),

		"insertions.bind.": prometheus.NewGauge(prometheus.GaugeOpts{
			Name: "dnsmasq_insertions",
			Help: "DNS cache insertions",
		}),

		"evictions.bind.": prometheus.NewGauge(prometheus.GaugeOpts{
			Name: "dnsmasq_evictions",
			Help: "DNS cache exictions: numbers of entries which replaced an unexpired cache entry",
		}),

		"misses.bind.": prometheus.NewGauge(prometheus.GaugeOpts{
			Name: "dnsmasq_misses",
			Help: "DNS cache misses: queries which had to be forwarded",
		}),

		"hits.bind.": prometheus.NewGauge(prometheus.GaugeOpts{
			Name: "dnsmasq_hits",
			Help: "DNS queries answered locally (cache hits)",
		}),

		"auth.bind.": prometheus.NewGauge(prometheus.GaugeOpts{
			Name: "dnsmasq_auth",
			Help: "DNS queries for authoritative zones",
		}),
	}

	leases = prometheus.NewGauge(prometheus.GaugeOpts{
		Name: "dnsmasq_leases",
		Help: "Number of DHCP leases handed out",
	})
)

func init() {
	for _, g := range floatMetrics {
		prometheus.MustRegister(g)
	}
	prometheus.MustRegister(leases)
}

// From https://manpages.debian.org/stretch/dnsmasq-base/dnsmasq.8.en.html:
// The cache statistics are also available in the DNS as answers to queries of
// class CHAOS and type TXT in domain bind. The domain names are cachesize.bind,
// insertions.bind, evictions.bind, misses.bind, hits.bind, auth.bind and
// servers.bind. An example command to query this, using the dig utility would
// be:
//     dig +short chaos txt cachesize.bind

func main() {
	flag.Parse()
	promHandler := promhttp.Handler()
	dnsClient := &dns.Client{
		SingleInflight: true,
	}
	http.HandleFunc("/metrics", func(w http.ResponseWriter, r *http.Request) {
		var eg errgroup.Group

		eg.Go(func() error {
			msg := &dns.Msg{
				MsgHdr: dns.MsgHdr{
					Id:               dns.Id(),
					RecursionDesired: true,
				},
				Question: []dns.Question{
					dns.Question{"cachesize.bind.", dns.TypeTXT, dns.ClassCHAOS},
					dns.Question{"insertions.bind.", dns.TypeTXT, dns.ClassCHAOS},
					dns.Question{"evictions.bind.", dns.TypeTXT, dns.ClassCHAOS},
					dns.Question{"misses.bind.", dns.TypeTXT, dns.ClassCHAOS},
					dns.Question{"hits.bind.", dns.TypeTXT, dns.ClassCHAOS},
					dns.Question{"auth.bind.", dns.TypeTXT, dns.ClassCHAOS},
					dns.Question{"servers.bind.", dns.TypeTXT, dns.ClassCHAOS},
				},
			}
			in, _, err := dnsClient.Exchange(msg, "127.0.0.1:53")
			if err != nil {
				return err
			}
			for _, a := range in.Answer {
				txt, ok := a.(*dns.TXT)
				if !ok {
					continue
				}
				switch txt.Hdr.Name {
				case "servers.bind.":
					// TODO: parse <server> <successes> <errors>, also with multiple upstreams
				default:
					g, ok := floatMetrics[txt.Hdr.Name]
					if !ok {
						continue // ignore unexpected answer from dnsmasq
					}
					if got, want := len(txt.Txt), 1; got != want {
						return fmt.Errorf("stats DNS record %q: unexpected number of replies: got %d, want %d", txt.Hdr.Name, got, want)
					}
					f, err := strconv.ParseFloat(txt.Txt[0], 64)
					if err != nil {
						return err
					}
					g.Set(f)
				}
			}
			return nil
		})

		eg.Go(func() error {
			f, err := os.Open(*leasesPath)
			if err != nil {
				return err
			}
			defer f.Close()
			scanner := bufio.NewScanner(f)
			var lines float64
			for scanner.Scan() {
				lines++
			}
			if err := scanner.Err(); err != nil {
				return err
			}
			leases.Set(lines)
			return nil
		})

		if err := eg.Wait(); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		promHandler.ServeHTTP(w, r)
	})
	log.Fatal(http.ListenAndServe(*listen, nil))
}
<commit_msg>implement -dnsmasq address flag<commit_after>// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Binary dnsmasq_exporter is a Prometheus exporter for dnsmasq statistics.
package main

import (
	"bufio"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"strconv"

	"golang.org/x/sync/errgroup"

	"github.com/miekg/dns"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

var (
	listen = flag.String("listen",
		"localhost:9153",
		"listen address")

	leasesPath = flag.String("leases_path",
		"/var/lib/misc/dnsmasq.leases",
		"path to the dnsmasq leases file")

	dnsmasqAddr = flag.String("dnsmasq",
		"localhost:53",
		"dnsmasq host:port address")
)

var (
	// floatMetrics contains prometheus Gauges, keyed by the stats DNS record
	// they correspond to.
	floatMetrics = map[string]prometheus.Gauge{
		"cachesize.bind.": prometheus.NewGauge(prometheus.GaugeOpts{
			Name: "dnsmasq_cachesize",
			Help: "configured size of the DNS cache",
		}),

		"insertions.bind.": prometheus.NewGauge(prometheus.GaugeOpts{
			Name: "dnsmasq_insertions",
			Help: "DNS cache insertions",
		}),

		"evictions.bind.": prometheus.NewGauge(prometheus.GaugeOpts{
			Name: "dnsmasq_evictions",
			Help: "DNS cache exictions: numbers of entries which replaced an unexpired cache entry",
		}),

		"misses.bind.": prometheus.NewGauge(prometheus.GaugeOpts{
			Name: "dnsmasq_misses",
			Help: "DNS cache misses: queries which had to be forwarded",
		}),

		"hits.bind.": prometheus.NewGauge(prometheus.GaugeOpts{
			Name: "dnsmasq_hits",
			Help: "DNS queries answered locally (cache hits)",
		}),

		"auth.bind.": prometheus.NewGauge(prometheus.GaugeOpts{
			Name: "dnsmasq_auth",
			Help: "DNS queries for authoritative zones",
		}),
	}

	leases = prometheus.NewGauge(prometheus.GaugeOpts{
		Name: "dnsmasq_leases",
		Help: "Number of DHCP leases handed out",
	})
)

func init() {
	for _, g := range floatMetrics {
		prometheus.MustRegister(g)
	}
	prometheus.MustRegister(leases)
}

// From https://manpages.debian.org/stretch/dnsmasq-base/dnsmasq.8.en.html:
// The cache statistics are also available in the DNS as answers to queries of
// class CHAOS and type TXT in domain bind. The domain names are cachesize.bind,
// insertions.bind, evictions.bind, misses.bind, hits.bind, auth.bind and
// servers.bind. An example command to query this, using the dig utility would
// be:
//     dig +short chaos txt cachesize.bind

func main() {
	flag.Parse()
	promHandler := promhttp.Handler()
	dnsClient := &dns.Client{
		SingleInflight: true,
	}
	http.HandleFunc("/metrics", func(w http.ResponseWriter, r *http.Request) {
		var eg errgroup.Group

		eg.Go(func() error {
			msg := &dns.Msg{
				MsgHdr: dns.MsgHdr{
					Id:               dns.Id(),
					RecursionDesired: true,
				},
				Question: []dns.Question{
					dns.Question{"cachesize.bind.", dns.TypeTXT, dns.ClassCHAOS},
					dns.Question{"insertions.bind.", dns.TypeTXT, dns.ClassCHAOS},
					dns.Question{"evictions.bind.", dns.TypeTXT, dns.ClassCHAOS},
					dns.Question{"misses.bind.", dns.TypeTXT, dns.ClassCHAOS},
					dns.Question{"hits.bind.", dns.TypeTXT, dns.ClassCHAOS},
					dns.Question{"auth.bind.", dns.TypeTXT, dns.ClassCHAOS},
					dns.Question{"servers.bind.", dns.TypeTXT, dns.ClassCHAOS},
				},
			}
			in, _, err := dnsClient.Exchange(msg, *dnsmasqAddr)
			if err != nil {
				return err
			}
			for _, a := range in.Answer {
				txt, ok := a.(*dns.TXT)
				if !ok {
					continue
				}
				switch txt.Hdr.Name {
				case "servers.bind.":
					// TODO: parse <server> <successes> <errors>, also with multiple upstreams
				default:
					g, ok := floatMetrics[txt.Hdr.Name]
					if !ok {
						continue // ignore unexpected answer from dnsmasq
					}
					if got, want := len(txt.Txt), 1; got != want {
						return fmt.Errorf("stats DNS record %q: unexpected number of replies: got %d, want %d", txt.Hdr.Name, got, want)
					}
					f, err := strconv.ParseFloat(txt.Txt[0], 64)
					if err != nil {
						return err
					}
					g.Set(f)
				}
			}
			return nil
		})

		eg.Go(func() error {
			f, err := os.Open(*leasesPath)
			if err != nil {
				return err
			}
			defer f.Close()
			scanner := bufio.NewScanner(f)
			var lines float64
			for scanner.Scan() {
				lines++
			}
			if err := scanner.Err(); err != nil {
				return err
			}
			leases.Set(lines)
			return nil
		})

		if err := eg.Wait(); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		promHandler.ServeHTTP(w, r)
	})
	log.Fatal(http.ListenAndServe(*listen, nil))
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("ByDimension API", func() {
	Context("Simple requests with 2 services", func() {
		var es *Elasticsearch
		var index_name string
		var api *ByDimensionApi
		BeforeEach(func() {
			index_name = "packetbeat-test"
			es = NewElasticsearch()

			_, err := es.DeleteIndex(index_name)
			Expect(err).To(BeNil())
			es.Refresh(index_name)

			transactions := []TestTransaction{
				TestTransaction{
					Timestamp:    "2006-01-02T15:04:05.000000",
					Service:      "service1",
					Host:         "Host0",
					Count:        2,
					Responsetime: 2000,
					Status:       "ok",
				},
				TestTransaction{
					Timestamp:    "2006-01-02T15:04:05.001000",
					Service:      "service2",
					Host:         "Host3",
					Count:        4,
					Responsetime: 2000,
					Status:       "ok",
				},
				TestTransaction{
					Timestamp:    "2006-01-02T15:04:05.001000",
					Service:      "service1",
					Host:         "host2",
					Count:        3,
					Responsetime: 2100,
					Status:       "error",
				},
			}

			err = transactionsInsertInto(es, index_name, transactions)
			Expect(err).To(BeNil())

			api = NewByDimensionApi(index_name)

		})
		AfterEach(func() {
			_, err := es.DeleteIndex(index_name)
			Expect(err).To(BeNil())
			es.Refresh(index_name)
		})

		It("should get", func() {
			var req ByDimensionRequest
			req.Timerange.From = "now-1d"
			req.Timerange.To = "now"
			req.Metrics = []string{"volume", "rt_avg", "rt_max",
				"rt_percentiles", "secondary_count", "errors_rate"}
			req.Config.Percentiles = []float32{50, 99.995}

			resp, err := api.Query(&req)
			Expect(err).To(BeNil())
			Expect(len(resp.Primary)).To(Equal(2))

			fmt.Println("response: ", resp)

			services := map[string]PrimaryDimension{}
			for _, primary := range resp.Primary {
				services[primary.Name] = primary
			}

			By("correct volumes")
			Expect(services["service1"].Metrics["volume"]).To(BeNumerically("~", 5))
			Expect(services["service2"].Metrics["volume"]).To(BeNumerically("~", 4))

			By("correct response times max and avg")
			Expect(services["service1"].Metrics["rt_max"]).To(BeNumerically("~", 2100))
			Expect(services["service2"].Metrics["rt_max"]).To(BeNumerically("~", 2000))
			Expect(services["service1"].Metrics["rt_avg"]).To(BeNumerically("~", 2050))
			Expect(services["service2"].Metrics["rt_avg"]).To(BeNumerically("~", 2000))

			By("correct percentiles")
			Expect(services["service1"].Metrics["rt_50.0p"]).To(BeNumerically("~", 2050))
			Expect(services["service2"].Metrics["rt_50.0p"]).To(BeNumerically("~", 2000))
			Expect(services["service2"].Metrics["rt_99.995p"]).To(BeNumerically("~", 2000))

			By("correct hosts values")
			Expect(services["service1"].Metrics["secondary_count"]).To(BeNumerically("~", 2))
			Expect(services["service2"].Metrics["secondary_count"]).To(BeNumerically("~", 1))

			By("correct errors rate")
			Expect(services["service1"].Metrics["errors_rate"]).To(BeNumerically("~",
				0.6, 1e-6))
			Expect(services["service2"].Metrics["errors_rate"]).To(BeNumerically("~", 0))
		})
	})
})
<commit_msg>Added more tests for the bydimension query<commit_after>package main

import (
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("ByDimension API", func() {
	Context("Simple requests with 2 services", func() {
		var es *Elasticsearch
		var index_name string
		var api *ByDimensionApi
		BeforeEach(func() {
			index_name = "packetbeat-test"
			es = NewElasticsearch()

			_, err := es.DeleteIndex(index_name)
			Expect(err).To(BeNil())
			es.Refresh(index_name)

			transactions := []TestTransaction{
				TestTransaction{
					Timestamp:    "2006-01-02T15:04:05.000000",
					Service:      "service1",
					Host:         "Host0",
					Count:        2,
					Responsetime: 2000,
					Status:       "ok",
				},
				TestTransaction{
					Timestamp:    "2006-01-02T15:04:05.001000",
					Service:      "service2",
					Host:         "Host3",
					Count:        4,
					Responsetime: 2000,
					Status:       "ok",
				},
				TestTransaction{
					Timestamp:    "2006-01-02T15:04:05.001000",
					Service:      "service1",
					Host:         "host2",
					Count:        3,
					Responsetime: 2100,
					Status:       "error",
				},
			}

			err = transactionsInsertInto(es, index_name, transactions)
			Expect(err).To(BeNil())

			api = NewByDimensionApi(index_name)

		})
		AfterEach(func() {
			_, err := es.DeleteIndex(index_name)
			Expect(err).To(BeNil())
			es.Refresh(index_name)
		})

		It("should get", func() {
			var req ByDimensionRequest
			req.Timerange.From = "now-1d"
			req.Timerange.To = "now"
			req.Metrics = []string{"volume", "rt_avg", "rt_max",
				"rt_percentiles", "secondary_count", "errors_rate"}
			req.Config.Percentiles = []float32{50, 99.995}

			resp, err := api.Query(&req)
			Expect(err).To(BeNil())
			Expect(len(resp.Primary)).To(Equal(2))

			services := map[string]PrimaryDimension{}
			for _, primary := range resp.Primary {
				services[primary.Name] = primary
			}

			By("correct volumes")
			Expect(services["service1"].Metrics["volume"]).To(BeNumerically("~", 5))
			Expect(services["service2"].Metrics["volume"]).To(BeNumerically("~", 4))

			By("correct response times max and avg")
			Expect(services["service1"].Metrics["rt_max"]).To(BeNumerically("~", 2100))
			Expect(services["service2"].Metrics["rt_max"]).To(BeNumerically("~", 2000))
			Expect(services["service1"].Metrics["rt_avg"]).To(BeNumerically("~", 2050))
			Expect(services["service2"].Metrics["rt_avg"]).To(BeNumerically("~", 2000))

			By("correct percentiles")
			Expect(services["service1"].Metrics["rt_50.0p"]).To(BeNumerically("~", 2050))
			Expect(services["service2"].Metrics["rt_50.0p"]).To(BeNumerically("~", 2000))
			Expect(services["service2"].Metrics["rt_99.995p"]).To(BeNumerically("~", 2000))

			By("correct hosts values")
			Expect(services["service1"].Metrics["secondary_count"]).To(BeNumerically("~", 2))
			Expect(services["service2"].Metrics["secondary_count"]).To(BeNumerically("~", 1))

			By("correct errors rate")
			Expect(services["service1"].Metrics["errors_rate"]).To(BeNumerically("~",
				0.6, 1e-6))
			Expect(services["service2"].Metrics["errors_rate"]).To(BeNumerically("~", 0))
		})

		It("should get error rate if only that is requested", func() {
			var req ByDimensionRequest
			req.Metrics = []string{"errors_rate"}

			resp, err := api.Query(&req)
			Expect(err).To(BeNil())
			Expect(len(resp.Primary)).To(Equal(2))

			services := map[string]PrimaryDimension{}
			for _, primary := range resp.Primary {
				services[primary.Name] = primary
			}

			Expect(services["service1"].Metrics["errors_rate"]).To(BeNumerically("~",
				0.6, 1e-6))
			Expect(services["service2"].Metrics["errors_rate"]).To(BeNumerically("~", 0))
		})

		It("should get 1.0 error rates when nothing is successful", func() {
			var req ByDimensionRequest
			req.Metrics = []string{"errors_rate"}
			req.Config.Status_value_ok = "nothing"

			resp, err := api.Query(&req)
			Expect(err).To(BeNil())
			Expect(len(resp.Primary)).To(Equal(2))

			services := map[string]PrimaryDimension{}
			for _, primary := range resp.Primary {
				services[primary.Name] = primary
			}

			Expect(services["service1"].Metrics["errors_rate"]).To(BeNumerically("~",
				1.0, 1e-6))
			Expect(services["service2"].Metrics["errors_rate"]).To(BeNumerically("~",
				1.0))
		})

		It("should return error when the metric is not defined", func() {
			var req ByDimensionRequest
			req.Metrics = []string{"something"}
			_, err := api.Query(&req)
			Expect(err).NotTo(BeNil())
		})

	})
})
<|endoftext|>


<commit_before>package core

import (
	"bytes"
	"crypto/sha1"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"
	"time"
)

// Root of the repository
var RepoRoot string

// Initial working directory
var initialWorkingDir string

// Initial subdir of the working directory, ie. what package did we start in.
var initialPackage string

const DirPermissions = os.ModeDir | 0775

// FindRepoRoot returns the root directory of the current repo and sets the initial working dir.
// Dies on failure if 'die' is set.
func FindRepoRoot(die bool) {
	initialWorkingDir, _ = os.Getwd()
	RepoRoot, initialPackage = getRepoRoot(die)
}

// getRepoRoot returns the root directory of the current repo and the initial package.
func getRepoRoot(die bool) (string, string) {
	dir, err := os.Getwd()
	if err != nil {
		log.Fatalf("Couldn't determine working directory: %s", err)
	}
	// Walk up directories looking for a .plzconfig file, which we use to identify the root.
	initial := dir
	for dir != "" {
		if PathExists(path.Join(dir, ConfigFileName)) {
			return dir, strings.TrimLeft(initial[len(dir):], "/")
		}
		dir, _ = path.Split(dir)
		dir = strings.TrimRight(dir, "/")
	}
	if die {
		log.Fatalf("Couldn't locate the repo root. Are you sure you're inside a plz repo?")
	}
	return "", ""
}

// Returns true if the build was initiated from the repo root.
// Used to provide slightly nicer output in some places.
func StartedAtRepoRoot() bool {
	return RepoRoot == initialWorkingDir
}

func PathExists(filename string) bool {
	_, err := os.Stat(filename)
	return err == nil
}

func FileExists(filename string) bool {
	info, err := os.Stat(filename)
	return err == nil && !info.IsDir()
}

// CopyFile copies a file from 'from' to 'to', with an attempt to perform a copy & rename
// to avoid chaos if anything goes wrong partway.
func CopyFile(from string, to string, mode os.FileMode) error {
	fromFile, err := os.Open(from)
	if err != nil {
		return err
	}
	defer fromFile.Close()
	return WriteFile(fromFile, to, mode)
}

// WriteFile writes data from a reader to the file named 'to', with an attempt to perform
// a copy & rename to avoid chaos if anything goes wrong partway.
func WriteFile(fromFile io.Reader, to string, mode os.FileMode) error {
	if err := os.RemoveAll(to); err != nil {
		return err
	}
	dir, file := path.Split(to)
	if err := os.MkdirAll(dir, DirPermissions); err != nil {
		return err
	}
	tempFile, err := ioutil.TempFile(dir, file)
	if err != nil {
		return err
	}
	if _, err := io.Copy(tempFile, fromFile); err != nil {
		return err
	}
	if err := tempFile.Close(); err != nil {
		return err
	}
	// OK, now file is written; adjust permissions appropriately.
	if mode == 0 {
		mode = 0664
	}
	if err := os.Chmod(tempFile.Name(), mode); err != nil {
		return err
	}
	// And move it to its final destination.
	return os.Rename(tempFile.Name(), to)
}

// Copies either a single file or a directory.
// If 'link' is true then we'll attempt to hardlink files instead of copying them
// (on failure we still attempt a copy).
func RecursiveCopyFile(from string, to string, mode os.FileMode, link bool) error {
	if info, err := os.Stat(from); err == nil && info.IsDir() {
		return filepath.Walk(from, func(name string, info os.FileInfo, err error) error {
			dest := path.Join(to, name[len(from):])
			if err != nil {
				return err
			} else if info.IsDir() {
				return os.MkdirAll(dest, DirPermissions)
			} else if (info.Mode() & os.ModeSymlink) != 0 {
				fi, err := os.Stat(name)
				if err != nil {
					return err
				}
				if fi.IsDir() {
					return RecursiveCopyFile(name+"/", dest+"/", mode, link)
				} else {
					return copyOrLinkFile(name, dest, mode, link)
				}
			} else {
				return copyOrLinkFile(name, dest, mode, link)
			}
		})
	} else {
		return copyOrLinkFile(from, to, mode, link)
	}
}

// Either copies or hardlinks a file based on the link argument.
func copyOrLinkFile(from, to string, mode os.FileMode, link bool) error {
	if link {
		if err := os.Link(from, to); err == nil {
			return nil
		}
	}
	return CopyFile(from, to, mode)
}

// TimeoutError is the type of error that's issued when a command times out.
type TimeoutError int

// Error formats this error as a string.
func (i TimeoutError) Error() string {
	return fmt.Sprintf("Timeout (%d seconds) exceeded", i)
}

// ExecWithTimeout runs an external command with a timeout.
// If the command times out the returned error will be a TimeoutError.
func ExecWithTimeout(cmd *exec.Cmd, timeout int, defaultTimeout int) ([]byte, error) {
	var out bytes.Buffer
	if timeout == 0 {
		timeout = defaultTimeout
	}
	cmd.Stdout = &out
	cmd.Stderr = &out
	ch := make(chan error)
	go func() { ch <- cmd.Run() }()
	select {
	case <-time.After(time.Duration(timeout) * time.Second):
		if err := cmd.Process.Kill(); err != nil {
			log.Errorf("Process %d could not be killed after exceeding timeout of %d seconds", cmd.Process.Pid, timeout)
		}
		return out.Bytes(), TimeoutError(timeout)
	case err := <-ch:
		return out.Bytes(), err
	}
}

type sourcePair struct{ Src, Tmp string }

// IterSources returns all the sources for a function, allowing for sources that are other rules
// and rules that require transitive dependencies.
// Yielded values are pairs of the original source location and its temporary location for this rule.
func IterSources(graph *BuildGraph, target *BuildTarget) <-chan sourcePair {
	ch := make(chan sourcePair)
	done := map[BuildLabel]bool{}
	donePaths := map[string]bool{}
	tmpDir := target.TmpDir()
	var inner func(dependency *BuildTarget)
	inner = func(dependency *BuildTarget) {
		if target == dependency {
			// This is the current build rule, so link its sources.
			for _, source := range dependency.AllSources() {
				fullPaths := source.FullPaths(graph)
				for i, sourcePath := range source.Paths(graph) {
					tmpPath := path.Join(tmpDir, sourcePath)
					ch <- sourcePair{fullPaths[i], tmpPath}
					donePaths[tmpPath] = true
				}
			}
		} else {
			// This is a dependency of the rule, so link its outputs.
			for _, dep := range dependency.Outputs() {
				depPath := path.Join(dependency.OutDir(), dep)
				tmpPath := path.Join(tmpDir, dependency.Label.PackageName, dep)
				if !donePaths[tmpPath] {
					ch <- sourcePair{depPath, tmpPath}
					donePaths[tmpPath] = true
				}
			}
			// Mark any label-type outputs as done.
			for _, out := range dependency.DeclaredOutputs() {
				if LooksLikeABuildLabel(out) {
					label := ParseBuildLabel(out, target.Label.PackageName)
					done[label] = true
				}
			}
		}
		done[dependency.Label] = true
		if target == dependency || (target.NeedsTransitiveDependencies && !dependency.OutputIsComplete) {
			for _, dep := range dependency.Dependencies() {
				if !done[dep.Label] && !target.IsTool(dep.Label) {
					inner(dep)
				}
			}
		} else {
			for _, dep := range dependency.ExportedDependencies() {
				for _, dep2 := range recursivelyProvideFor(graph, target, dep) {
					if !done[dep2] {
						inner(graph.TargetOrDie(dep2))
					}
				}
			}
		}
	}
	go func() {
		inner(target)
		close(ch)
	}()
	return ch
}

// recursivelyProvideFor recursively applies ProvideFor to a target.
func recursivelyProvideFor(graph *BuildGraph, target *BuildTarget, dep BuildLabel) []BuildLabel {
	ret := graph.TargetOrDie(dep).ProvideFor(target)
	if len(ret) == 1 && ret[0] == dep {
		return ret // Providing itself, don't recurse
	}
	ret2 := []BuildLabel{}
	for _, r := range ret {
		ret2 = append(ret2, recursivelyProvideFor(graph, target, r)...)
	}
	return ret2
}

// Yields all the runtime files for a rule (outputs & data files), similar to above.
func IterRuntimeFiles(graph *BuildGraph, target *BuildTarget, absoluteOuts bool) <-chan sourcePair {
	done := map[string]bool{}
	ch := make(chan sourcePair)

	makeOut := func(out string) string {
		if absoluteOuts {
			return path.Join(RepoRoot, target.TestDir(), out)
		} else {
			return out
		}
	}

	pushOut := func(src, out string) {
		out = makeOut(out)
		if !done[out] {
			ch <- sourcePair{src, out}
			done[out] = true
		}
	}

	var inner func(*BuildTarget)
	inner = func(target *BuildTarget) {
		for _, out := range target.Outputs() {
			pushOut(path.Join(target.OutDir(), out), out)
		}
		for _, data := range target.Data {
			fullPaths := data.FullPaths(graph)
			for i, dataPath := range data.Paths(graph) {
				pushOut(fullPaths[i], dataPath)
			}
			if label := data.Label(); label != nil {
				for _, dep := range graph.TargetOrDie(*label).ExportedDependencies() {
					inner(graph.TargetOrDie(dep))
				}
			}
		}
		for _, dep := range target.ExportedDependencies() {
			inner(graph.TargetOrDie(dep))
		}
	}
	go func() {
		inner(target)
		close(ch)
	}()
	return ch
}

// Yields all the transitive input files for a rule (sources & data files), similar to above (again).
func IterInputPaths(graph *BuildGraph, target *BuildTarget) <-chan string {
	// Use a couple of maps to protect us from dep-graph loops and to stop parsing the same target
	// multiple times. We also only want to push files to the channel that it has not already seen.
	donePaths := map[string]bool{}
	doneTargets := map[*BuildTarget]bool{}
	ch := make(chan string)
	var inner func(*BuildTarget)
	inner = func(target *BuildTarget) {

		if !doneTargets[target] {
			// First yield all the sources of the target only ever pushing declared paths to
			// the channel to prevent us outputting any intermediate files.
			for _, source := range target.AllSources() {
				// If the label is nil add any input paths contained here.
				if label := source.Label(); label == nil {
					for _, sourcePath := range source.FullPaths(graph) {
						if !donePaths[sourcePath] {
							ch <- sourcePath
							donePaths[sourcePath] = true
						}
					}
					// Otherwise we should recurse for this build label (and gather its sources)
				} else {
					inner(graph.TargetOrDie(*label))
				}
			}

			// Now yield all the data deps of this rule.
			for _, data := range target.Data {
				// If the label is nil add any input paths contained here.
				if label := data.Label(); label == nil {
					for _, sourcePath := range data.FullPaths(graph) {
						if !donePaths[sourcePath] {
							ch <- sourcePath
							donePaths[sourcePath] = true
						}
					}
					// Otherwise we should recurse for this build label (and gather its sources)
				} else {
					inner(graph.TargetOrDie(*label))
				}
			}

			// Finally recurse for all the deps of this rule.
			for _, dep := range target.Dependencies() {
				inner(dep)
			}
			doneTargets[target] = true
		}
	}
	go func() {
		inner(target)
		close(ch)
	}()
	return ch
}

// Symlinks a single source file for a build rule.
func PrepareSource(sourcePath string, tmpPath string) error {
	dir := path.Dir(tmpPath)
	if !PathExists(dir) {
		if err := os.MkdirAll(dir, DirPermissions); err != nil {
			return err
		}
	}
	if !PathExists(sourcePath) {
		return fmt.Errorf("Source file %s doesn't exist", sourcePath)
	}
	return RecursiveCopyFile(sourcePath, tmpPath, 0, true)
}

func PrepareSourcePair(pair sourcePair) error {
	if path.IsAbs(pair.Src) {
		return PrepareSource(pair.Src, pair.Tmp)
	}
	return PrepareSource(path.Join(RepoRoot, pair.Src), pair.Tmp)
}

func PostBuildOutputFileName(target *BuildTarget) string {
	return ".build_output_" + target.Label.Name
}

// CollapseHash combines our usual four-part hash into one by XOR'ing them together.
// This helps keep things short in places where sometimes we get complaints about filenames being too long (?)
// and where we don't especially care about breaking out the individual parts of hashes, which
// is important for many parts of the system.
func CollapseHash(key []byte) []byte {
	short := [sha1.Size]byte{}
	// We store the rule hash twice, if it's repeated we must make sure not to xor it
	// against itself.
	if bytes.Equal(key[0:sha1.Size], key[sha1.Size:2*sha1.Size]) {
		for i := 0; i < sha1.Size; i++ {
			short[i] = key[i] ^ key[i+2*sha1.Size] ^ key[i+3*sha1.Size]
		}
	} else {
		for i := 0; i < sha1.Size; i++ {
			short[i] = key[i] ^ key[i+sha1.Size] ^ key[i+2*sha1.Size] ^ key[i+3*sha1.Size]
		}
	}
	return short[:]
}
<commit_msg>Fix bug where tools of transitive dependencies could turn up in the tmp dir for targets with need_transitive_deps = True. (#81)<commit_after>package core

import (
	"bytes"
	"crypto/sha1"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"
	"time"
)

// Root of the repository
var RepoRoot string

// Initial working directory
var initialWorkingDir string

// Initial subdir of the working directory, ie. what package did we start in.
var initialPackage string

const DirPermissions = os.ModeDir | 0775

// FindRepoRoot returns the root directory of the current repo and sets the initial working dir.
// Dies on failure if 'die' is set.
func FindRepoRoot(die bool) {
	initialWorkingDir, _ = os.Getwd()
	RepoRoot, initialPackage = getRepoRoot(die)
}

// getRepoRoot returns the root directory of the current repo and the initial package.
func getRepoRoot(die bool) (string, string) {
	dir, err := os.Getwd()
	if err != nil {
		log.Fatalf("Couldn't determine working directory: %s", err)
	}
	// Walk up directories looking for a .plzconfig file, which we use to identify the root.
	initial := dir
	for dir != "" {
		if PathExists(path.Join(dir, ConfigFileName)) {
			return dir, strings.TrimLeft(initial[len(dir):], "/")
		}
		dir, _ = path.Split(dir)
		dir = strings.TrimRight(dir, "/")
	}
	if die {
		log.Fatalf("Couldn't locate the repo root. Are you sure you're inside a plz repo?")
	}
	return "", ""
}

// Returns true if the build was initiated from the repo root.
// Used to provide slightly nicer output in some places.
func StartedAtRepoRoot() bool {
	return RepoRoot == initialWorkingDir
}

func PathExists(filename string) bool {
	_, err := os.Stat(filename)
	return err == nil
}

func FileExists(filename string) bool {
	info, err := os.Stat(filename)
	return err == nil && !info.IsDir()
}

// CopyFile copies a file from 'from' to 'to', with an attempt to perform a copy & rename
// to avoid chaos if anything goes wrong partway.
func CopyFile(from string, to string, mode os.FileMode) error {
	fromFile, err := os.Open(from)
	if err != nil {
		return err
	}
	defer fromFile.Close()
	return WriteFile(fromFile, to, mode)
}

// WriteFile writes data from a reader to the file named 'to', with an attempt to perform
// a copy & rename to avoid chaos if anything goes wrong partway.
func WriteFile(fromFile io.Reader, to string, mode os.FileMode) error {
	if err := os.RemoveAll(to); err != nil {
		return err
	}
	dir, file := path.Split(to)
	if err := os.MkdirAll(dir, DirPermissions); err != nil {
		return err
	}
	tempFile, err := ioutil.TempFile(dir, file)
	if err != nil {
		return err
	}
	if _, err := io.Copy(tempFile, fromFile); err != nil {
		return err
	}
	if err := tempFile.Close(); err != nil {
		return err
	}
	// OK, now file is written; adjust permissions appropriately.
	if mode == 0 {
		mode = 0664
	}
	if err := os.Chmod(tempFile.Name(), mode); err != nil {
		return err
	}
	// And move it to its final destination.
	return os.Rename(tempFile.Name(), to)
}

// Copies either a single file or a directory.
// If 'link' is true then we'll attempt to hardlink files instead of copying them
// (on failure we still attempt a copy).
func RecursiveCopyFile(from string, to string, mode os.FileMode, link bool) error {
	if info, err := os.Stat(from); err == nil && info.IsDir() {
		return filepath.Walk(from, func(name string, info os.FileInfo, err error) error {
			dest := path.Join(to, name[len(from):])
			if err != nil {
				return err
			} else if info.IsDir() {
				return os.MkdirAll(dest, DirPermissions)
			} else if (info.Mode() & os.ModeSymlink) != 0 {
				fi, err := os.Stat(name)
				if err != nil {
					return err
				}
				if fi.IsDir() {
					return RecursiveCopyFile(name+"/", dest+"/", mode, link)
				} else {
					return copyOrLinkFile(name, dest, mode, link)
				}
			} else {
				return copyOrLinkFile(name, dest, mode, link)
			}
		})
	} else {
		return copyOrLinkFile(from, to, mode, link)
	}
}

// Either copies or hardlinks a file based on the link argument.
func copyOrLinkFile(from, to string, mode os.FileMode, link bool) error {
	if link {
		if err := os.Link(from, to); err == nil {
			return nil
		}
	}
	return CopyFile(from, to, mode)
}

// TimeoutError is the type of error that's issued when a command times out.
type TimeoutError int

// Error formats this error as a string.
func (i TimeoutError) Error() string {
	return fmt.Sprintf("Timeout (%d seconds) exceeded", i)
}

// ExecWithTimeout runs an external command with a timeout.
// If the command times out the returned error will be a TimeoutError.
func ExecWithTimeout(cmd *exec.Cmd, timeout int, defaultTimeout int) ([]byte, error) {
	var out bytes.Buffer
	if timeout == 0 {
		timeout = defaultTimeout
	}
	cmd.Stdout = &out
	cmd.Stderr = &out
	ch := make(chan error)
	go func() { ch <- cmd.Run() }()
	select {
	case <-time.After(time.Duration(timeout) * time.Second):
		if err := cmd.Process.Kill(); err != nil {
			log.Errorf("Process %d could not be killed after exceeding timeout of %d seconds", cmd.Process.Pid, timeout)
		}
		return out.Bytes(), TimeoutError(timeout)
	case err := <-ch:
		return out.Bytes(), err
	}
}

type sourcePair struct{ Src, Tmp string }

// IterSources returns all the sources for a function, allowing for sources that are other rules
// and rules that require transitive dependencies.
// Yielded values are pairs of the original source location and its temporary location for this rule.
func IterSources(graph *BuildGraph, target *BuildTarget) <-chan sourcePair {
	ch := make(chan sourcePair)
	done := map[BuildLabel]bool{}
	donePaths := map[string]bool{}
	tmpDir := target.TmpDir()
	var inner func(dependency *BuildTarget)
	inner = func(dependency *BuildTarget) {
		if target == dependency {
			// This is the current build rule, so link its sources.
			for _, source := range dependency.AllSources() {
				fullPaths := source.FullPaths(graph)
				for i, sourcePath := range source.Paths(graph) {
					tmpPath := path.Join(tmpDir, sourcePath)
					ch <- sourcePair{fullPaths[i], tmpPath}
					donePaths[tmpPath] = true
				}
			}
		} else {
			// This is a dependency of the rule, so link its outputs.
			for _, dep := range dependency.Outputs() {
				depPath := path.Join(dependency.OutDir(), dep)
				tmpPath := path.Join(tmpDir, dependency.Label.PackageName, dep)
				if !donePaths[tmpPath] {
					ch <- sourcePair{depPath, tmpPath}
					donePaths[tmpPath] = true
				}
			}
			// Mark any label-type outputs as done.
			for _, out := range dependency.DeclaredOutputs() {
				if LooksLikeABuildLabel(out) {
					label := ParseBuildLabel(out, target.Label.PackageName)
					done[label] = true
				}
			}
		}
		done[dependency.Label] = true
		if target == dependency || (target.NeedsTransitiveDependencies && !dependency.OutputIsComplete) {
			for _, dep := range dependency.Dependencies() {
				if !done[dep.Label] && !dependency.IsTool(dep.Label) {
					inner(dep)
				}
			}
		} else {
			for _, dep := range dependency.ExportedDependencies() {
				for _, dep2 := range recursivelyProvideFor(graph, target, dep) {
					if !done[dep2] {
						inner(graph.TargetOrDie(dep2))
					}
				}
			}
		}
	}
	go func() {
		inner(target)
		close(ch)
	}()
	return ch
}

// recursivelyProvideFor recursively applies ProvideFor to a target.
func recursivelyProvideFor(graph *BuildGraph, target *BuildTarget, dep BuildLabel) []BuildLabel {
	ret := graph.TargetOrDie(dep).ProvideFor(target)
	if len(ret) == 1 && ret[0] == dep {
		return ret // Providing itself, don't recurse
	}
	ret2 := []BuildLabel{}
	for _, r := range ret {
		ret2 = append(ret2, recursivelyProvideFor(graph, target, r)...)
	}
	return ret2
}

// Yields all the runtime files for a rule (outputs & data files), similar to above.
func IterRuntimeFiles(graph *BuildGraph, target *BuildTarget, absoluteOuts bool) <-chan sourcePair {
	done := map[string]bool{}
	ch := make(chan sourcePair)

	makeOut := func(out string) string {
		if absoluteOuts {
			return path.Join(RepoRoot, target.TestDir(), out)
		} else {
			return out
		}
	}

	pushOut := func(src, out string) {
		out = makeOut(out)
		if !done[out] {
			ch <- sourcePair{src, out}
			done[out] = true
		}
	}

	var inner func(*BuildTarget)
	inner = func(target *BuildTarget) {
		for _, out := range target.Outputs() {
			pushOut(path.Join(target.OutDir(), out), out)
		}
		for _, data := range target.Data {
			fullPaths := data.FullPaths(graph)
			for i, dataPath := range data.Paths(graph) {
				pushOut(fullPaths[i], dataPath)
			}
			if label := data.Label(); label != nil {
				for _, dep := range graph.TargetOrDie(*label).ExportedDependencies() {
					inner(graph.TargetOrDie(dep))
				}
			}
		}
		for _, dep := range target.ExportedDependencies() {
			inner(graph.TargetOrDie(dep))
		}
	}
	go func() {
		inner(target)
		close(ch)
	}()
	return ch
}

// Yields all the transitive input files for a rule (sources & data files), similar to above (again).
func IterInputPaths(graph *BuildGraph, target *BuildTarget) <-chan string {
	// Use a couple of maps to protect us from dep-graph loops and to stop parsing the same target
	// multiple times. We also only want to push files to the channel that it has not already seen.
	donePaths := map[string]bool{}
	doneTargets := map[*BuildTarget]bool{}
	ch := make(chan string)
	var inner func(*BuildTarget)
	inner = func(target *BuildTarget) {

		if !doneTargets[target] {
			// First yield all the sources of the target only ever pushing declared paths to
			// the channel to prevent us outputting any intermediate files.
			for _, source := range target.AllSources() {
				// If the label is nil add any input paths contained here.
				if label := source.Label(); label == nil {
					for _, sourcePath := range source.FullPaths(graph) {
						if !donePaths[sourcePath] {
							ch <- sourcePath
							donePaths[sourcePath] = true
						}
					}
					// Otherwise we should recurse for this build label (and gather its sources)
				} else {
					inner(graph.TargetOrDie(*label))
				}
			}

			// Now yield all the data deps of this rule.
			for _, data := range target.Data {
				// If the label is nil add any input paths contained here.
				if label := data.Label(); label == nil {
					for _, sourcePath := range data.FullPaths(graph) {
						if !donePaths[sourcePath] {
							ch <- sourcePath
							donePaths[sourcePath] = true
						}
					}
					// Otherwise we should recurse for this build label (and gather its sources)
				} else {
					inner(graph.TargetOrDie(*label))
				}
			}

			// Finally recurse for all the deps of this rule.
			for _, dep := range target.Dependencies() {
				inner(dep)
			}
			doneTargets[target] = true
		}
	}
	go func() {
		inner(target)
		close(ch)
	}()
	return ch
}

// Symlinks a single source file for a build rule.
func PrepareSource(sourcePath string, tmpPath string) error {
	dir := path.Dir(tmpPath)
	if !PathExists(dir) {
		if err := os.MkdirAll(dir, DirPermissions); err != nil {
			return err
		}
	}
	if !PathExists(sourcePath) {
		return fmt.Errorf("Source file %s doesn't exist", sourcePath)
	}
	return RecursiveCopyFile(sourcePath, tmpPath, 0, true)
}

func PrepareSourcePair(pair sourcePair) error {
	if path.IsAbs(pair.Src) {
		return PrepareSource(pair.Src, pair.Tmp)
	}
	return PrepareSource(path.Join(RepoRoot, pair.Src), pair.Tmp)
}

func PostBuildOutputFileName(target *BuildTarget) string {
	return ".build_output_" + target.Label.Name
}

// CollapseHash combines our usual four-part hash into one by XOR'ing them together.
// This helps keep things short in places where sometimes we get complaints about filenames being too long (?)
// and where we don't especially care about breaking out the individual parts of hashes, which
// is important for many parts of the system.
func CollapseHash(key []byte) []byte {
	short := [sha1.Size]byte{}
	// We store the rule hash twice, if it's repeated we must make sure not to xor it
	// against itself.
	if bytes.Equal(key[0:sha1.Size], key[sha1.Size:2*sha1.Size]) {
		for i := 0; i < sha1.Size; i++ {
			short[i] = key[i] ^ key[i+2*sha1.Size] ^ key[i+3*sha1.Size]
		}
	} else {
		for i := 0; i < sha1.Size; i++ {
			short[i] = key[i] ^ key[i+sha1.Size] ^ key[i+2*sha1.Size] ^ key[i+3*sha1.Size]
		}
	}
	return short[:]
}
<|endoftext|>


<commit_before>package dos

import (
	"fmt"
	"path/filepath"
	"regexp"
	"strings"
	"syscall"
	"unicode"
	"unsafe"
)

var msvcrt = syscall.NewLazyDLL("msvcrt")
var _chdrive = msvcrt.NewProc("_chdrive")
var _wchdir = msvcrt.NewProc("_wchdir")

func chdrive_(n rune) uintptr {
	rc, _, _ := _chdrive.Call(uintptr(n & 0x1F))
	return rc
}

func getFirst(s string) (rune, error) {
	reader := strings.NewReader(s)
	drive, _, err := reader.ReadRune()
	if err != nil {
		return 0, err
	}
	return unicode.ToUpper(drive), nil
}

// Change drive without changing the working directory there.
func Chdrive(drive string) error {
	driveLetter, driveErr := getFirst(drive)
	if driveErr != nil {
		return driveErr
	}
	chdrive_(driveLetter)
	return nil
}

var rxPath = regexp.MustCompile("^([a-zA-Z]):(.*)$")

// Change the current working directory
// without changeing the working directory
// in the last drive.
func Chdir(folder_ string) error {
	folder := folder_
	if m := rxPath.FindStringSubmatch(folder_); m != nil {
		status := chdrive_(rune(m[1][0]))
		if status != 0 {
			return fmt.Errorf("%s: no such directory", folder_)
		}
		folder = m[2]
		if len(folder) <= 0 {
			return nil
		}
	}
	utf16, err := syscall.UTF16PtrFromString(folder)
	if err == nil {
		status, _, _ := _wchdir.Call(uintptr(unsafe.Pointer(utf16)))
		if status != 0 {
			err = fmt.Errorf("%s: no such directory", folder_)
		}
	}
	return err
}

var rxRoot = regexp.MustCompile("^([a-zA-Z]:)?[/\\\\]")
var rxDrive = regexp.MustCompile("^[a-zA-Z]:")

func joinPath2(a, b string) string {
	if len(a) <= 0 || rxRoot.MatchString(b) || rxDrive.MatchString(b) {
		return b
	}
	switch a[len(a)-1] {
	case '\\', '/', ':':
		return a + b
	default:
		return a + "\\" + b
	}
}

// Equals filepath.Join but this works right when path has drive-letter.
func Join(paths ...string) string {
	result := paths[len(paths)-1]
	for i := len(paths) - 2; i >= 0; i-- {
		result = joinPath2(paths[i], result)
	}
	return result
}

// Expand filenames matching with wildcard-pattern.
func Glob(pattern string) ([]string, error) {
	pname := filepath.Base(pattern)
	if strings.IndexAny(pname, "*?") < 0 {
		return nil, nil
	}
	findf, err := FindFirst(pattern)
	if err != nil {
		return nil, err
	}
	defer findf.Close()
	dirname := filepath.Dir(pattern)
	match := make([]string, 0, 100)
	for {
		if name := findf.Name(); name[0] != '.' || pname[0] == '.' {
			match = append(match, filepath.Join(dirname, name))
		}
		err := findf.FindNext()
		if err != nil {
			return match, nil
		}
	}
}
<commit_msg>Make code compact dos.Glob()<commit_after>package dos

import (
	"fmt"
	"path/filepath"
	"regexp"
	"strings"
	"syscall"
	"unicode"
	"unsafe"
)

var msvcrt = syscall.NewLazyDLL("msvcrt")
var _chdrive = msvcrt.NewProc("_chdrive")
var _wchdir = msvcrt.NewProc("_wchdir")

func chdrive_(n rune) uintptr {
	rc, _, _ := _chdrive.Call(uintptr(n & 0x1F))
	return rc
}

func getFirst(s string) (rune, error) {
	reader := strings.NewReader(s)
	drive, _, err := reader.ReadRune()
	if err != nil {
		return 0, err
	}
	return unicode.ToUpper(drive), nil
}

// Change drive without changing the working directory there.
func Chdrive(drive string) error {
	driveLetter, driveErr := getFirst(drive)
	if driveErr != nil {
		return driveErr
	}
	chdrive_(driveLetter)
	return nil
}

var rxPath = regexp.MustCompile("^([a-zA-Z]):(.*)$")

// Change the current working directory
// without changeing the working directory
// in the last drive.
func Chdir(folder_ string) error {
	folder := folder_
	if m := rxPath.FindStringSubmatch(folder_); m != nil {
		status := chdrive_(rune(m[1][0]))
		if status != 0 {
			return fmt.Errorf("%s: no such directory", folder_)
		}
		folder = m[2]
		if len(folder) <= 0 {
			return nil
		}
	}
	utf16, err := syscall.UTF16PtrFromString(folder)
	if err == nil {
		status, _, _ := _wchdir.Call(uintptr(unsafe.Pointer(utf16)))
		if status != 0 {
			err = fmt.Errorf("%s: no such directory", folder_)
		}
	}
	return err
}

var rxRoot = regexp.MustCompile("^([a-zA-Z]:)?[/\\\\]")
var rxDrive = regexp.MustCompile("^[a-zA-Z]:")

func joinPath2(a, b string) string {
	if len(a) <= 0 || rxRoot.MatchString(b) || rxDrive.MatchString(b) {
		return b
	}
	switch a[len(a)-1] {
	case '\\', '/', ':':
		return a + b
	default:
		return a + "\\" + b
	}
}

// Equals filepath.Join but this works right when path has drive-letter.
func Join(paths ...string) string {
	result := paths[len(paths)-1]
	for i := len(paths) - 2; i >= 0; i-- {
		result = joinPath2(paths[i], result)
	}
	return result
}

// Expand filenames matching with wildcard-pattern.
func Glob(pattern string) ([]string, error) {
	pname := filepath.Base(pattern)
	if strings.IndexAny(pname, "*?") < 0 {
		return nil, nil
	}
	match := make([]string, 0, 100)
	dirname := filepath.Dir(pattern)
	err := ForFiles(pattern, func(findf *FindFiles) bool {
		if name := findf.Name(); name[0] != '.' || pname[0] == '.' {
			match = append(match, filepath.Join(dirname, name))
		}
		return true
	})
	return match, err
}
<|endoftext|>