github.com/kaydxh/golang@v0.0.131/go/net/url/url.go (about) 1 /* 2 *Copyright (c) 2022, kaydxh 3 * 4 *Permission is hereby granted, free of charge, to any person obtaining a copy 5 *of this software and associated documentation files (the "Software"), to deal 6 *in the Software without restriction, including without limitation the rights 7 *to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 *copies of the Software, and to permit persons to whom the Software is 9 *furnished to do so, subject to the following conditions: 10 * 11 *The above copyright notice and this permission notice shall be included in all 12 *copies or substantial portions of the Software. 13 * 14 *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 *IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 *FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 *AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 *LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 *OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 *SOFTWARE. 21 */ 22 package url 23 24 import ( 25 "bytes" 26 "context" 27 "fmt" 28 "reflect" 29 "strconv" 30 "sync" 31 ) 32 33 type Client struct { 34 mutex sync.Mutex 35 buffer *bytes.Buffer 36 37 opts struct { 38 needEmptyValue bool 39 urlCodec UrlCodec 40 } 41 } 42 43 func New(ctx context.Context, options ...ClientOption) (*Client, error) { 44 c := &Client{} 45 c.opts.urlCodec = DefaultUrlCodec{} 46 c.ApplyOptions(options...) 47 48 return c, nil 49 } 50 51 //can convert struct to url encode for url paratment 52 func (c *Client) Encode(data interface{}) (string, error) { 53 c.mutex.Lock() 54 defer c.mutex.Unlock() 55 56 c.buffer = new(bytes.Buffer) 57 rv := reflect.ValueOf(data) 58 59 err := c.build(rv, "", reflect.Interface) 60 if err != nil { 61 return "", err 62 } 63 64 buf := c.buffer.Bytes() 65 c.buffer = nil 66 67 return string(buf[0 : len(buf)-1]), nil 68 69 } 70 71 func (c *Client) encode(rv reflect.Value) (string, error) { 72 encoder := getEncoder(rv.Kind()) 73 if encoder == nil { 74 return "", fmt.Errorf("unsupport type: %v", rv.Type().String()) 75 } 76 77 return encoder.Encode(rv), nil 78 } 79 80 func (c *Client) build( 81 rv reflect.Value, 82 parentKey string, 83 parentKind reflect.Kind, 84 ) error { 85 fmt.Println("-- parentKey: ", parentKey) 86 switch rv.Kind() { 87 case reflect.Map: 88 for _, key := range rv.MapKeys() { 89 checkKey := key 90 if key.Kind() == reflect.Interface || key.Kind() == reflect.Ptr { 91 checkKey = checkKey.Elem() 92 } 93 94 keyStr, err := c.encode(checkKey) 95 if err != nil { 96 return err 97 } 98 99 c.build(rv.MapIndex(key), keyStr, rv.Kind()) 100 101 } 102 103 case reflect.Slice, reflect.Array: 104 for i := 0; i < rv.Len(); i++ { 105 c.build(rv.Index(i), parentKey+"["+strconv.Itoa(i)+"]", rv.Kind()) 106 } 107 108 case reflect.Struct: 109 rt := rv.Type() 110 for i := 0; i < rt.NumField(); i++ { 111 ft := rt.Field(i) 112 //unexported 113 if ft.PkgPath != "" && !ft.Anonymous { 114 continue 115 } 116 117 //specially handle anonymous fields 118 if ft.Anonymous && rv.Field(i).Kind() == reflect.Struct { 119 c.build(rv.Field(i), parentKey, rv.Kind()) 120 continue 121 } 122 123 /* 124 tag := ft.Tag.Get("query") 125 //all ignore 126 if tag == "-" { 127 continue 128 } 129 130 t := newTag(tag) 131 //get the related name 132 name := t.getName() 133 if name == "" { 134 name = ft.Name 135 } 136 */ 137 138 name := ft.Name 139 c.build(rv.Field(i), name, rv.Kind()) 140 } 141 142 case reflect.Ptr, reflect.Interface: 143 if !rv.IsNil() { 144 c.build(rv.Elem(), parentKey, parentKind) 145 } 146 147 default: 148 c.appendKeyValue(parentKey, rv, parentKind) 149 150 } 151 152 return nil 153 154 } 155 156 //basic structure can be translated directly 157 func (c *Client) appendKeyValue(key string, rv reflect.Value, parentKind reflect.Kind) error { 158 //If parent type is struct and empty value will be ignored by default. unless needEmptyValue is true. 159 if parentKind == reflect.Struct && !c.opts.needEmptyValue && isEmptyValue(rv) { 160 return nil 161 } 162 163 //If parent type is slice or array, then repack key. eg. students[0] -> students[] 164 if parentKind == reflect.Slice || parentKind == reflect.Array { 165 key = repackArrayQueryKey(key) 166 } 167 168 s, err := c.encode(rv) 169 if err != nil { 170 return err 171 } 172 173 _, err = c.buffer.WriteString( 174 c.opts.urlCodec.Escape(key) + "=" + c.opts.urlCodec.Escape(s) + "&", 175 ) 176 177 return err 178 } 179 180 //Is Zero-value 181 func isEmptyValue(v reflect.Value) bool { 182 switch v.Kind() { 183 case reflect.Array, reflect.Map, reflect.Slice, reflect.String: 184 return v.Len() == 0 185 case reflect.Bool: 186 return !v.Bool() 187 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 188 return v.Int() == 0 189 case reflect.Uint, 190 reflect.Uint8, 191 reflect.Uint16, 192 reflect.Uint32, 193 reflect.Uint64, 194 reflect.Uintptr: 195 return v.Uint() == 0 196 case reflect.Float32, reflect.Float64: 197 return v.Float() == 0 198 case reflect.Interface, reflect.Ptr: 199 return v.IsNil() 200 } 201 return false 202 } 203 204 //if key like `students[0]` , repack it to `students[]` 205 func repackArrayQueryKey(key string) string { 206 l := len(key) 207 if l > 0 && key[l-1] == ']' { 208 for l--; l >= 0; l-- { 209 if key[l] == '[' { 210 return key[:l+1] + "]" 211 } 212 } 213 } 214 return key 215 }