github.com/morlay/goqcloud@v0.0.0-20181123023149-b00e0b0b9afc/transform/parameter_scanner.go (about)

     1  package transform
     2  
     3  import (
     4  	"encoding"
     5  	"errors"
     6  	"fmt"
     7  	"go/ast"
     8  	"net/url"
     9  	"reflect"
    10  	"strconv"
    11  	"strings"
    12  )
    13  
    14  func NewParameterScanner() *ParameterScanner {
    15  	return &ParameterScanner{
    16  		params: url.Values{},
    17  	}
    18  }
    19  
    20  type ParameterScanner struct {
    21  	walker PathWalker
    22  	params url.Values
    23  }
    24  
    25  func (s *ParameterScanner) SetParam(key string, value string) {
    26  	if value != "" {
    27  		s.params.Set(key, value)
    28  	}
    29  }
    30  
    31  func (s *ParameterScanner) Scan(rv reflect.Value) error {
    32  	v := rv.Interface()
    33  	if rv.Kind() != reflect.Ptr {
    34  		if rv.CanAddr() {
    35  			v = rv.Addr().Interface()
    36  		}
    37  	} else if rv.IsNil() {
    38  		return nil
    39  	}
    40  
    41  	if stringer, ok := v.(fmt.Stringer); ok {
    42  		s.SetParam(s.walker.Current(), stringer.String())
    43  		return nil
    44  	}
    45  
    46  	if textMarshaler, ok := v.(encoding.TextMarshaler); ok {
    47  		text, err := textMarshaler.MarshalText()
    48  		if err != nil {
    49  			return err
    50  		}
    51  
    52  		s.SetParam(s.walker.Current(), string(text))
    53  		return nil
    54  	}
    55  
    56  	rv = reflect.Indirect(rv)
    57  
    58  	switch rv.Kind() {
    59  	case reflect.Struct:
    60  		tpe := rv.Type()
    61  
    62  		for i := 0; i < tpe.NumField(); i++ {
    63  			field := tpe.Field(i)
    64  			if !ast.IsExported(field.Name) {
    65  				continue
    66  			}
    67  
    68  			name := field.Name
    69  
    70  			if jsonTag, exists := field.Tag.Lookup("json"); exists {
    71  				name, _ = tagAngFlags(jsonTag)
    72  			}
    73  
    74  			if nameTag, exists := field.Tag.Lookup("name"); exists {
    75  				name, _ = tagAngFlags(nameTag)
    76  			}
    77  
    78  			if (name != "-") || field.Anonymous {
    79  				if !field.Anonymous {
    80  					s.walker.Enter(name)
    81  				}
    82  
    83  				if err := s.Scan(rv.Field(i)); err != nil {
    84  					return err
    85  				}
    86  
    87  				if !field.Anonymous {
    88  					s.walker.Exit()
    89  				}
    90  			}
    91  		}
    92  	case reflect.Slice, reflect.Array:
    93  		for i := 0; i < rv.Len(); i++ {
    94  			s.walker.Enter(i)
    95  			if err := s.Scan(rv.Index(i)); err != nil {
    96  				return err
    97  			}
    98  			s.walker.Exit()
    99  		}
   100  	default:
   101  		v := rv.Interface()
   102  
   103  		switch rv.Kind() {
   104  		case reflect.String:
   105  			s.SetParam(s.walker.Current(), v.(string))
   106  		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint8:
   107  			s.SetParam(s.walker.Current(), fmt.Sprintf("%d", v))
   108  		case reflect.Bool:
   109  			s.SetParam(s.walker.Current(), strconv.FormatBool(v.(bool)))
   110  		case reflect.Float32:
   111  			s.SetParam(s.walker.Current(), strconv.FormatFloat(float64(v.(float32)), 'f', -1, 32))
   112  		case reflect.Float64:
   113  			s.SetParam(s.walker.Current(), strconv.FormatFloat(v.(float64), 'f', -1, 64))
   114  		default:
   115  			return ErrorUnSupportedType
   116  		}
   117  	}
   118  	return nil
   119  }
   120  
   121  var (
   122  	ErrorUnSupportedType = errors.New("unsupported type")
   123  )
   124  
   125  func (s *ParameterScanner) Params() url.Values {
   126  	return s.params
   127  }
   128  
   129  func tagAngFlags(tag string) (string, map[string]bool) {
   130  	values := strings.Split(tag, ",")
   131  
   132  	name := values[0]
   133  	flags := map[string]bool{}
   134  
   135  	if len(values) > 1 {
   136  		for _, v := range values[1:] {
   137  			flags[v] = true
   138  		}
   139  	}
   140  
   141  	return name, flags
   142  }