github.com/bytedance/go-tagexpr@v2.7.5-0.20210114074101-de5b8743ad85+incompatible/binding/tag_names.go (about)

     1  package binding
     2  
     3  import (
     4  	"net/textproto"
     5  	"reflect"
     6  	"sort"
     7  	"strings"
     8  
     9  	"github.com/henrylee2cn/goutil"
    10  )
    11  
    12  const (
    13  	tagRequired         = "required"
    14  	tagRequired2        = "req"
    15  	defaultTagPath      = "path"
    16  	defaultTagQuery     = "query"
    17  	defaultTagHeader    = "header"
    18  	defaultTagCookie    = "cookie"
    19  	defaultTagRawbody   = "raw_body"
    20  	defaultTagForm      = "form"
    21  	defaultTagValidator = "vd"
    22  	tagProtobuf         = "protobuf"
    23  	tagJSON             = "json"
    24  	tagDefault          = "default"
    25  )
    26  
    27  // Config the struct tag naming and so on
    28  type Config struct {
    29  	// LooseZeroMode if set to true,
    30  	// the empty string request parameter is bound to the zero value of parameter.
    31  	// NOTE: Suitable for these parameter types: query/header/cookie/form .
    32  	LooseZeroMode bool
    33  	// PathParam use 'path' by default when empty
    34  	PathParam string
    35  	// Query use 'query' by default when empty
    36  	Query string
    37  	// Header use 'header' by default when empty
    38  	Header string
    39  	// Cookie use 'cookie' by default when empty
    40  	Cookie string
    41  	// RawBody use 'raw' by default when empty
    42  	RawBody string
    43  	// FormBody use 'form' by default when empty
    44  	FormBody string
    45  	// Validator use 'vd' by default when empty
    46  	Validator string
    47  	// protobufBody use 'protobuf' by default when empty
    48  	protobufBody string
    49  	// jsonBody use 'json' by default when empty
    50  	jsonBody string
    51  	// defaultVal use 'default' by default when empty
    52  	defaultVal string
    53  
    54  	list []string
    55  }
    56  
    57  func (t *Config) init() {
    58  	t.list = []string{
    59  		goutil.InitAndGetString(&t.PathParam, defaultTagPath),
    60  		goutil.InitAndGetString(&t.Query, defaultTagQuery),
    61  		goutil.InitAndGetString(&t.Header, defaultTagHeader),
    62  		goutil.InitAndGetString(&t.Cookie, defaultTagCookie),
    63  		goutil.InitAndGetString(&t.RawBody, defaultTagRawbody),
    64  		goutil.InitAndGetString(&t.FormBody, defaultTagForm),
    65  		goutil.InitAndGetString(&t.Validator, defaultTagValidator),
    66  		goutil.InitAndGetString(&t.protobufBody, tagProtobuf),
    67  		goutil.InitAndGetString(&t.jsonBody, tagJSON),
    68  		goutil.InitAndGetString(&t.defaultVal, tagDefault),
    69  	}
    70  }
    71  
    72  func (t *Config) parse(field reflect.StructField) tagKVs {
    73  	tag := field.Tag
    74  	fieldName := field.Name
    75  
    76  	kvs := make(tagKVs, 0, len(t.list))
    77  	s := string(tag)
    78  
    79  	for _, name := range t.list {
    80  		value, ok := tag.Lookup(name)
    81  		if !ok {
    82  			continue
    83  		}
    84  		if name != t.defaultVal && value != "-" {
    85  			value = strings.Replace(strings.TrimSpace(value), " ", "", -1)
    86  			value = strings.Replace(value, "\t", "", -1)
    87  			if name == t.RawBody {
    88  				info := newTagInfo(value, false)
    89  				if info.required || info.paramName == tagRequired {
    90  					value = "," + tagRequired
    91  				}
    92  			} else if value == "" {
    93  				value = fieldName
    94  			} else if value == ","+tagRequired {
    95  				value = fieldName + value
    96  			}
    97  		}
    98  		kvs = append(kvs, &tagKV{name: name, value: value, pos: strings.Index(s, name)})
    99  	}
   100  	sort.Sort(kvs)
   101  	return kvs
   102  }
   103  
   104  type tagKV struct {
   105  	name  string
   106  	value string
   107  	pos   int
   108  }
   109  
   110  type tagInfo struct {
   111  	paramIn   in
   112  	paramName string
   113  	required  bool
   114  	namePath  string
   115  
   116  	requiredError, typeError, cannotError, contentTypeError error
   117  }
   118  
   119  func (t *tagKV) toInfo(isHeader bool) *tagInfo {
   120  	return newTagInfo(t.value, isHeader)
   121  }
   122  
   123  func newTagInfo(value string, isHeader bool) *tagInfo {
   124  	info := new(tagInfo)
   125  	for i, v := range strings.Split(value, ",") {
   126  		v = strings.TrimSpace(v)
   127  		if i == 0 {
   128  			info.paramName = v
   129  		} else {
   130  			if v == tagRequired || v == tagRequired2 {
   131  				info.required = true
   132  			}
   133  		}
   134  	}
   135  	if isHeader {
   136  		info.paramName = textproto.CanonicalMIMEHeaderKey(info.paramName)
   137  	}
   138  	return info
   139  }
   140  
   141  type tagKVs []*tagKV
   142  
   143  // Len is the number of elements in the collection.
   144  func (a tagKVs) Len() int {
   145  	return len(a)
   146  }
   147  
   148  // Less reports whether the element with
   149  // index i should sort before the element with index j.
   150  func (a tagKVs) Less(i, j int) bool {
   151  	return a[i].pos < a[j].pos
   152  }
   153  
   154  // Swap swaps the elements with indexes i and j.
   155  func (a tagKVs) Swap(i, j int) {
   156  	a[i], a[j] = a[j], a[i]
   157  }
   158  
   159  func (a tagKVs) lookup(name string) (string, bool) {
   160  	for _, v := range a {
   161  		if v.name == name {
   162  			return v.value, true
   163  		}
   164  	}
   165  	return "", false
   166  }