586 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			586 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Copyright 2012 Gary Burd
 | 
						|
//
 | 
						|
// 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 redis
 | 
						|
 | 
						|
import (
 | 
						|
	"errors"
 | 
						|
	"fmt"
 | 
						|
	"reflect"
 | 
						|
	"strconv"
 | 
						|
	"strings"
 | 
						|
	"sync"
 | 
						|
)
 | 
						|
 | 
						|
func ensureLen(d reflect.Value, n int) {
 | 
						|
	if n > d.Cap() {
 | 
						|
		d.Set(reflect.MakeSlice(d.Type(), n, n))
 | 
						|
	} else {
 | 
						|
		d.SetLen(n)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func cannotConvert(d reflect.Value, s interface{}) error {
 | 
						|
	var sname string
 | 
						|
	switch s.(type) {
 | 
						|
	case string:
 | 
						|
		sname = "Redis simple string"
 | 
						|
	case Error:
 | 
						|
		sname = "Redis error"
 | 
						|
	case int64:
 | 
						|
		sname = "Redis integer"
 | 
						|
	case []byte:
 | 
						|
		sname = "Redis bulk string"
 | 
						|
	case []interface{}:
 | 
						|
		sname = "Redis array"
 | 
						|
	default:
 | 
						|
		sname = reflect.TypeOf(s).String()
 | 
						|
	}
 | 
						|
	return fmt.Errorf("cannot convert from %s to %s", sname, d.Type())
 | 
						|
}
 | 
						|
 | 
						|
func convertAssignBulkString(d reflect.Value, s []byte) (err error) {
 | 
						|
	switch d.Type().Kind() {
 | 
						|
	case reflect.Float32, reflect.Float64:
 | 
						|
		var x float64
 | 
						|
		x, err = strconv.ParseFloat(string(s), d.Type().Bits())
 | 
						|
		d.SetFloat(x)
 | 
						|
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | 
						|
		var x int64
 | 
						|
		x, err = strconv.ParseInt(string(s), 10, d.Type().Bits())
 | 
						|
		d.SetInt(x)
 | 
						|
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
 | 
						|
		var x uint64
 | 
						|
		x, err = strconv.ParseUint(string(s), 10, d.Type().Bits())
 | 
						|
		d.SetUint(x)
 | 
						|
	case reflect.Bool:
 | 
						|
		var x bool
 | 
						|
		x, err = strconv.ParseBool(string(s))
 | 
						|
		d.SetBool(x)
 | 
						|
	case reflect.String:
 | 
						|
		d.SetString(string(s))
 | 
						|
	case reflect.Slice:
 | 
						|
		if d.Type().Elem().Kind() != reflect.Uint8 {
 | 
						|
			err = cannotConvert(d, s)
 | 
						|
		} else {
 | 
						|
			d.SetBytes(s)
 | 
						|
		}
 | 
						|
	default:
 | 
						|
		err = cannotConvert(d, s)
 | 
						|
	}
 | 
						|
	return
 | 
						|
}
 | 
						|
 | 
						|
func convertAssignInt(d reflect.Value, s int64) (err error) {
 | 
						|
	switch d.Type().Kind() {
 | 
						|
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | 
						|
		d.SetInt(s)
 | 
						|
		if d.Int() != s {
 | 
						|
			err = strconv.ErrRange
 | 
						|
			d.SetInt(0)
 | 
						|
		}
 | 
						|
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
 | 
						|
		if s < 0 {
 | 
						|
			err = strconv.ErrRange
 | 
						|
		} else {
 | 
						|
			x := uint64(s)
 | 
						|
			d.SetUint(x)
 | 
						|
			if d.Uint() != x {
 | 
						|
				err = strconv.ErrRange
 | 
						|
				d.SetUint(0)
 | 
						|
			}
 | 
						|
		}
 | 
						|
	case reflect.Bool:
 | 
						|
		d.SetBool(s != 0)
 | 
						|
	default:
 | 
						|
		err = cannotConvert(d, s)
 | 
						|
	}
 | 
						|
	return
 | 
						|
}
 | 
						|
 | 
						|
func convertAssignValue(d reflect.Value, s interface{}) (err error) {
 | 
						|
	if d.Kind() != reflect.Ptr {
 | 
						|
		if d.CanAddr() {
 | 
						|
			d2 := d.Addr()
 | 
						|
			if d2.CanInterface() {
 | 
						|
				if scanner, ok := d2.Interface().(Scanner); ok {
 | 
						|
					return scanner.RedisScan(s)
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	} else if d.CanInterface() {
 | 
						|
		// Already a reflect.Ptr
 | 
						|
		if d.IsNil() {
 | 
						|
			d.Set(reflect.New(d.Type().Elem()))
 | 
						|
		}
 | 
						|
		if scanner, ok := d.Interface().(Scanner); ok {
 | 
						|
			return scanner.RedisScan(s)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	switch s := s.(type) {
 | 
						|
	case []byte:
 | 
						|
		err = convertAssignBulkString(d, s)
 | 
						|
	case int64:
 | 
						|
		err = convertAssignInt(d, s)
 | 
						|
	default:
 | 
						|
		err = cannotConvert(d, s)
 | 
						|
	}
 | 
						|
	return err
 | 
						|
}
 | 
						|
 | 
						|
func convertAssignArray(d reflect.Value, s []interface{}) error {
 | 
						|
	if d.Type().Kind() != reflect.Slice {
 | 
						|
		return cannotConvert(d, s)
 | 
						|
	}
 | 
						|
	ensureLen(d, len(s))
 | 
						|
	for i := 0; i < len(s); i++ {
 | 
						|
		if err := convertAssignValue(d.Index(i), s[i]); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func convertAssign(d interface{}, s interface{}) (err error) {
 | 
						|
	if scanner, ok := d.(Scanner); ok {
 | 
						|
		return scanner.RedisScan(s)
 | 
						|
	}
 | 
						|
 | 
						|
	// Handle the most common destination types using type switches and
 | 
						|
	// fall back to reflection for all other types.
 | 
						|
	switch s := s.(type) {
 | 
						|
	case nil:
 | 
						|
		// ignore
 | 
						|
	case []byte:
 | 
						|
		switch d := d.(type) {
 | 
						|
		case *string:
 | 
						|
			*d = string(s)
 | 
						|
		case *int:
 | 
						|
			*d, err = strconv.Atoi(string(s))
 | 
						|
		case *bool:
 | 
						|
			*d, err = strconv.ParseBool(string(s))
 | 
						|
		case *[]byte:
 | 
						|
			*d = s
 | 
						|
		case *interface{}:
 | 
						|
			*d = s
 | 
						|
		case nil:
 | 
						|
			// skip value
 | 
						|
		default:
 | 
						|
			if d := reflect.ValueOf(d); d.Type().Kind() != reflect.Ptr {
 | 
						|
				err = cannotConvert(d, s)
 | 
						|
			} else {
 | 
						|
				err = convertAssignBulkString(d.Elem(), s)
 | 
						|
			}
 | 
						|
		}
 | 
						|
	case int64:
 | 
						|
		switch d := d.(type) {
 | 
						|
		case *int:
 | 
						|
			x := int(s)
 | 
						|
			if int64(x) != s {
 | 
						|
				err = strconv.ErrRange
 | 
						|
				x = 0
 | 
						|
			}
 | 
						|
			*d = x
 | 
						|
		case *bool:
 | 
						|
			*d = s != 0
 | 
						|
		case *interface{}:
 | 
						|
			*d = s
 | 
						|
		case nil:
 | 
						|
			// skip value
 | 
						|
		default:
 | 
						|
			if d := reflect.ValueOf(d); d.Type().Kind() != reflect.Ptr {
 | 
						|
				err = cannotConvert(d, s)
 | 
						|
			} else {
 | 
						|
				err = convertAssignInt(d.Elem(), s)
 | 
						|
			}
 | 
						|
		}
 | 
						|
	case string:
 | 
						|
		switch d := d.(type) {
 | 
						|
		case *string:
 | 
						|
			*d = s
 | 
						|
		case *interface{}:
 | 
						|
			*d = s
 | 
						|
		case nil:
 | 
						|
			// skip value
 | 
						|
		default:
 | 
						|
			err = cannotConvert(reflect.ValueOf(d), s)
 | 
						|
		}
 | 
						|
	case []interface{}:
 | 
						|
		switch d := d.(type) {
 | 
						|
		case *[]interface{}:
 | 
						|
			*d = s
 | 
						|
		case *interface{}:
 | 
						|
			*d = s
 | 
						|
		case nil:
 | 
						|
			// skip value
 | 
						|
		default:
 | 
						|
			if d := reflect.ValueOf(d); d.Type().Kind() != reflect.Ptr {
 | 
						|
				err = cannotConvert(d, s)
 | 
						|
			} else {
 | 
						|
				err = convertAssignArray(d.Elem(), s)
 | 
						|
			}
 | 
						|
		}
 | 
						|
	case Error:
 | 
						|
		err = s
 | 
						|
	default:
 | 
						|
		err = cannotConvert(reflect.ValueOf(d), s)
 | 
						|
	}
 | 
						|
	return
 | 
						|
}
 | 
						|
 | 
						|
// Scan copies from src to the values pointed at by dest.
 | 
						|
//
 | 
						|
// Scan uses RedisScan if available otherwise:
 | 
						|
//
 | 
						|
// The values pointed at by dest must be an integer, float, boolean, string,
 | 
						|
// []byte, interface{} or slices of these types. Scan uses the standard strconv
 | 
						|
// package to convert bulk strings to numeric and boolean types.
 | 
						|
//
 | 
						|
// If a dest value is nil, then the corresponding src value is skipped.
 | 
						|
//
 | 
						|
// If a src element is nil, then the corresponding dest value is not modified.
 | 
						|
//
 | 
						|
// To enable easy use of Scan in a loop, Scan returns the slice of src
 | 
						|
// following the copied values.
 | 
						|
func Scan(src []interface{}, dest ...interface{}) ([]interface{}, error) {
 | 
						|
	if len(src) < len(dest) {
 | 
						|
		return nil, errors.New("redigo.Scan: array short")
 | 
						|
	}
 | 
						|
	var err error
 | 
						|
	for i, d := range dest {
 | 
						|
		err = convertAssign(d, src[i])
 | 
						|
		if err != nil {
 | 
						|
			err = fmt.Errorf("redigo.Scan: cannot assign to dest %d: %v", i, err)
 | 
						|
			break
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return src[len(dest):], err
 | 
						|
}
 | 
						|
 | 
						|
type fieldSpec struct {
 | 
						|
	name      string
 | 
						|
	index     []int
 | 
						|
	omitEmpty bool
 | 
						|
}
 | 
						|
 | 
						|
type structSpec struct {
 | 
						|
	m map[string]*fieldSpec
 | 
						|
	l []*fieldSpec
 | 
						|
}
 | 
						|
 | 
						|
func (ss *structSpec) fieldSpec(name []byte) *fieldSpec {
 | 
						|
	return ss.m[string(name)]
 | 
						|
}
 | 
						|
 | 
						|
func compileStructSpec(t reflect.Type, depth map[string]int, index []int, ss *structSpec) {
 | 
						|
	for i := 0; i < t.NumField(); i++ {
 | 
						|
		f := t.Field(i)
 | 
						|
		switch {
 | 
						|
		case f.PkgPath != "" && !f.Anonymous:
 | 
						|
			// Ignore unexported fields.
 | 
						|
		case f.Anonymous:
 | 
						|
			// TODO: Handle pointers. Requires change to decoder and
 | 
						|
			// protection against infinite recursion.
 | 
						|
			if f.Type.Kind() == reflect.Struct {
 | 
						|
				compileStructSpec(f.Type, depth, append(index, i), ss)
 | 
						|
			}
 | 
						|
		default:
 | 
						|
			fs := &fieldSpec{name: f.Name}
 | 
						|
			tag := f.Tag.Get("redis")
 | 
						|
			p := strings.Split(tag, ",")
 | 
						|
			if len(p) > 0 {
 | 
						|
				if p[0] == "-" {
 | 
						|
					continue
 | 
						|
				}
 | 
						|
				if len(p[0]) > 0 {
 | 
						|
					fs.name = p[0]
 | 
						|
				}
 | 
						|
				for _, s := range p[1:] {
 | 
						|
					switch s {
 | 
						|
					case "omitempty":
 | 
						|
						fs.omitEmpty = true
 | 
						|
					default:
 | 
						|
						panic(fmt.Errorf("redigo: unknown field tag %s for type %s", s, t.Name()))
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
			d, found := depth[fs.name]
 | 
						|
			if !found {
 | 
						|
				d = 1 << 30
 | 
						|
			}
 | 
						|
			switch {
 | 
						|
			case len(index) == d:
 | 
						|
				// At same depth, remove from result.
 | 
						|
				delete(ss.m, fs.name)
 | 
						|
				j := 0
 | 
						|
				for i := 0; i < len(ss.l); i++ {
 | 
						|
					if fs.name != ss.l[i].name {
 | 
						|
						ss.l[j] = ss.l[i]
 | 
						|
						j += 1
 | 
						|
					}
 | 
						|
				}
 | 
						|
				ss.l = ss.l[:j]
 | 
						|
			case len(index) < d:
 | 
						|
				fs.index = make([]int, len(index)+1)
 | 
						|
				copy(fs.index, index)
 | 
						|
				fs.index[len(index)] = i
 | 
						|
				depth[fs.name] = len(index)
 | 
						|
				ss.m[fs.name] = fs
 | 
						|
				ss.l = append(ss.l, fs)
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
var (
 | 
						|
	structSpecMutex  sync.RWMutex
 | 
						|
	structSpecCache  = make(map[reflect.Type]*structSpec)
 | 
						|
	defaultFieldSpec = &fieldSpec{}
 | 
						|
)
 | 
						|
 | 
						|
func structSpecForType(t reflect.Type) *structSpec {
 | 
						|
 | 
						|
	structSpecMutex.RLock()
 | 
						|
	ss, found := structSpecCache[t]
 | 
						|
	structSpecMutex.RUnlock()
 | 
						|
	if found {
 | 
						|
		return ss
 | 
						|
	}
 | 
						|
 | 
						|
	structSpecMutex.Lock()
 | 
						|
	defer structSpecMutex.Unlock()
 | 
						|
	ss, found = structSpecCache[t]
 | 
						|
	if found {
 | 
						|
		return ss
 | 
						|
	}
 | 
						|
 | 
						|
	ss = &structSpec{m: make(map[string]*fieldSpec)}
 | 
						|
	compileStructSpec(t, make(map[string]int), nil, ss)
 | 
						|
	structSpecCache[t] = ss
 | 
						|
	return ss
 | 
						|
}
 | 
						|
 | 
						|
var errScanStructValue = errors.New("redigo.ScanStruct: value must be non-nil pointer to a struct")
 | 
						|
 | 
						|
// ScanStruct scans alternating names and values from src to a struct. The
 | 
						|
// HGETALL and CONFIG GET commands return replies in this format.
 | 
						|
//
 | 
						|
// ScanStruct uses exported field names to match values in the response. Use
 | 
						|
// 'redis' field tag to override the name:
 | 
						|
//
 | 
						|
//      Field int `redis:"myName"`
 | 
						|
//
 | 
						|
// Fields with the tag redis:"-" are ignored.
 | 
						|
//
 | 
						|
// Each field uses RedisScan if available otherwise:
 | 
						|
// Integer, float, boolean, string and []byte fields are supported. Scan uses the
 | 
						|
// standard strconv package to convert bulk string values to numeric and
 | 
						|
// boolean types.
 | 
						|
//
 | 
						|
// If a src element is nil, then the corresponding field is not modified.
 | 
						|
func ScanStruct(src []interface{}, dest interface{}) error {
 | 
						|
	d := reflect.ValueOf(dest)
 | 
						|
	if d.Kind() != reflect.Ptr || d.IsNil() {
 | 
						|
		return errScanStructValue
 | 
						|
	}
 | 
						|
	d = d.Elem()
 | 
						|
	if d.Kind() != reflect.Struct {
 | 
						|
		return errScanStructValue
 | 
						|
	}
 | 
						|
	ss := structSpecForType(d.Type())
 | 
						|
 | 
						|
	if len(src)%2 != 0 {
 | 
						|
		return errors.New("redigo.ScanStruct: number of values not a multiple of 2")
 | 
						|
	}
 | 
						|
 | 
						|
	for i := 0; i < len(src); i += 2 {
 | 
						|
		s := src[i+1]
 | 
						|
		if s == nil {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		name, ok := src[i].([]byte)
 | 
						|
		if !ok {
 | 
						|
			return fmt.Errorf("redigo.ScanStruct: key %d not a bulk string value", i)
 | 
						|
		}
 | 
						|
		fs := ss.fieldSpec(name)
 | 
						|
		if fs == nil {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		if err := convertAssignValue(d.FieldByIndex(fs.index), s); err != nil {
 | 
						|
			return fmt.Errorf("redigo.ScanStruct: cannot assign field %s: %v", fs.name, err)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
var (
 | 
						|
	errScanSliceValue = errors.New("redigo.ScanSlice: dest must be non-nil pointer to a struct")
 | 
						|
)
 | 
						|
 | 
						|
// ScanSlice scans src to the slice pointed to by dest. The elements the dest
 | 
						|
// slice must be integer, float, boolean, string, struct or pointer to struct
 | 
						|
// values.
 | 
						|
//
 | 
						|
// Struct fields must be integer, float, boolean or string values. All struct
 | 
						|
// fields are used unless a subset is specified using fieldNames.
 | 
						|
func ScanSlice(src []interface{}, dest interface{}, fieldNames ...string) error {
 | 
						|
	d := reflect.ValueOf(dest)
 | 
						|
	if d.Kind() != reflect.Ptr || d.IsNil() {
 | 
						|
		return errScanSliceValue
 | 
						|
	}
 | 
						|
	d = d.Elem()
 | 
						|
	if d.Kind() != reflect.Slice {
 | 
						|
		return errScanSliceValue
 | 
						|
	}
 | 
						|
 | 
						|
	isPtr := false
 | 
						|
	t := d.Type().Elem()
 | 
						|
	if t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct {
 | 
						|
		isPtr = true
 | 
						|
		t = t.Elem()
 | 
						|
	}
 | 
						|
 | 
						|
	if t.Kind() != reflect.Struct {
 | 
						|
		ensureLen(d, len(src))
 | 
						|
		for i, s := range src {
 | 
						|
			if s == nil {
 | 
						|
				continue
 | 
						|
			}
 | 
						|
			if err := convertAssignValue(d.Index(i), s); err != nil {
 | 
						|
				return fmt.Errorf("redigo.ScanSlice: cannot assign element %d: %v", i, err)
 | 
						|
			}
 | 
						|
		}
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	ss := structSpecForType(t)
 | 
						|
	fss := ss.l
 | 
						|
	if len(fieldNames) > 0 {
 | 
						|
		fss = make([]*fieldSpec, len(fieldNames))
 | 
						|
		for i, name := range fieldNames {
 | 
						|
			fss[i] = ss.m[name]
 | 
						|
			if fss[i] == nil {
 | 
						|
				return fmt.Errorf("redigo.ScanSlice: ScanSlice bad field name %s", name)
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if len(fss) == 0 {
 | 
						|
		return errors.New("redigo.ScanSlice: no struct fields")
 | 
						|
	}
 | 
						|
 | 
						|
	n := len(src) / len(fss)
 | 
						|
	if n*len(fss) != len(src) {
 | 
						|
		return errors.New("redigo.ScanSlice: length not a multiple of struct field count")
 | 
						|
	}
 | 
						|
 | 
						|
	ensureLen(d, n)
 | 
						|
	for i := 0; i < n; i++ {
 | 
						|
		d := d.Index(i)
 | 
						|
		if isPtr {
 | 
						|
			if d.IsNil() {
 | 
						|
				d.Set(reflect.New(t))
 | 
						|
			}
 | 
						|
			d = d.Elem()
 | 
						|
		}
 | 
						|
		for j, fs := range fss {
 | 
						|
			s := src[i*len(fss)+j]
 | 
						|
			if s == nil {
 | 
						|
				continue
 | 
						|
			}
 | 
						|
			if err := convertAssignValue(d.FieldByIndex(fs.index), s); err != nil {
 | 
						|
				return fmt.Errorf("redigo.ScanSlice: cannot assign element %d to field %s: %v", i*len(fss)+j, fs.name, err)
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// Args is a helper for constructing command arguments from structured values.
 | 
						|
type Args []interface{}
 | 
						|
 | 
						|
// Add returns the result of appending value to args.
 | 
						|
func (args Args) Add(value ...interface{}) Args {
 | 
						|
	return append(args, value...)
 | 
						|
}
 | 
						|
 | 
						|
// AddFlat returns the result of appending the flattened value of v to args.
 | 
						|
//
 | 
						|
// Maps are flattened by appending the alternating keys and map values to args.
 | 
						|
//
 | 
						|
// Slices are flattened by appending the slice elements to args.
 | 
						|
//
 | 
						|
// Structs are flattened by appending the alternating names and values of
 | 
						|
// exported fields to args. If v is a nil struct pointer, then nothing is
 | 
						|
// appended. The 'redis' field tag overrides struct field names. See ScanStruct
 | 
						|
// for more information on the use of the 'redis' field tag.
 | 
						|
//
 | 
						|
// Other types are appended to args as is.
 | 
						|
func (args Args) AddFlat(v interface{}) Args {
 | 
						|
	rv := reflect.ValueOf(v)
 | 
						|
	switch rv.Kind() {
 | 
						|
	case reflect.Struct:
 | 
						|
		args = flattenStruct(args, rv)
 | 
						|
	case reflect.Slice:
 | 
						|
		for i := 0; i < rv.Len(); i++ {
 | 
						|
			args = append(args, rv.Index(i).Interface())
 | 
						|
		}
 | 
						|
	case reflect.Map:
 | 
						|
		for _, k := range rv.MapKeys() {
 | 
						|
			args = append(args, k.Interface(), rv.MapIndex(k).Interface())
 | 
						|
		}
 | 
						|
	case reflect.Ptr:
 | 
						|
		if rv.Type().Elem().Kind() == reflect.Struct {
 | 
						|
			if !rv.IsNil() {
 | 
						|
				args = flattenStruct(args, rv.Elem())
 | 
						|
			}
 | 
						|
		} else {
 | 
						|
			args = append(args, v)
 | 
						|
		}
 | 
						|
	default:
 | 
						|
		args = append(args, v)
 | 
						|
	}
 | 
						|
	return args
 | 
						|
}
 | 
						|
 | 
						|
func flattenStruct(args Args, v reflect.Value) Args {
 | 
						|
	ss := structSpecForType(v.Type())
 | 
						|
	for _, fs := range ss.l {
 | 
						|
		fv := v.FieldByIndex(fs.index)
 | 
						|
		if fs.omitEmpty {
 | 
						|
			var empty = false
 | 
						|
			switch fv.Kind() {
 | 
						|
			case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
 | 
						|
				empty = fv.Len() == 0
 | 
						|
			case reflect.Bool:
 | 
						|
				empty = !fv.Bool()
 | 
						|
			case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | 
						|
				empty = fv.Int() == 0
 | 
						|
			case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
 | 
						|
				empty = fv.Uint() == 0
 | 
						|
			case reflect.Float32, reflect.Float64:
 | 
						|
				empty = fv.Float() == 0
 | 
						|
			case reflect.Interface, reflect.Ptr:
 | 
						|
				empty = fv.IsNil()
 | 
						|
			}
 | 
						|
			if empty {
 | 
						|
				continue
 | 
						|
			}
 | 
						|
		}
 | 
						|
		args = append(args, fs.name, fv.Interface())
 | 
						|
	}
 | 
						|
	return args
 | 
						|
}
 |