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  }