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 }