github.com/searKing/golang/go@v1.2.117/reflect/struct_tag.go (about)

     1  // Copyright 2022 The searKing Author. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package reflect
     6  
     7  import (
     8  	"bytes"
     9  	"errors"
    10  	"sort"
    11  	"strconv"
    12  	"strings"
    13  
    14  	strings_ "github.com/searKing/golang/go/strings"
    15  )
    16  
    17  // cmd/vendor/golang.org/x/tools/go/analysis/passes/structtag/structtag.go
    18  var (
    19  	errTagSyntax        = errors.New("bad syntax for struct tag pair")
    20  	errTagKeySyntax     = errors.New("bad syntax for struct tag key")
    21  	errTagValueSyntax   = errors.New("bad syntax for struct tag value")
    22  	errTagValueSpace    = errors.New("suspicious space in struct tag value")
    23  	errTagSpace         = errors.New("key:\"value\" pairs not separated by spaces")
    24  	errTagDuplicatedKey = errors.New("duplicated for struct tag key")
    25  
    26  	errKeyNotSet   = errors.New("tag key does not exist")
    27  	errTagNotExist = errors.New("tag does not exist")
    28  )
    29  
    30  // A StructTag is the tag string in a struct field, as reflect.StructTag .
    31  //
    32  // By convention, tag strings are a concatenation of
    33  // optionally space-separated key:"value" pairs.
    34  // Each key is a non-empty string consisting of non-control
    35  // characters other than space (U+0020 ' '), quote (U+0022 '"'),
    36  // and colon (U+003A ':').  Each value is quoted using U+0022 '"'
    37  // characters and Go string literal syntax.
    38  type StructTag struct {
    39  	tagsByKey   map[string]SubStructTag
    40  	orderedKeys []string
    41  }
    42  
    43  // ParseAstStructTag parses a single struct field tag of AST and returns the set of subTags.
    44  // This code is based on the validateStructTag code in package
    45  // tag `json:"name,omitempty"`, field.Tag.Value returned by AST
    46  func ParseAstStructTag(tag string) (*StructTag, error) {
    47  	if tag != "" {
    48  		var err error
    49  		tag, err = strconv.Unquote(tag)
    50  		if err != nil {
    51  			return nil, err
    52  		}
    53  	}
    54  	return ParseStructTag(tag)
    55  }
    56  
    57  // ParseStructTag parses a single struct field tag and returns the set of subTags.
    58  // This code is based on the validateStructTag code in package
    59  // tag json:"name,omitempty", reflect.StructField.Tag returned by reflect
    60  func ParseStructTag(tag string) (*StructTag, error) {
    61  	// This code is based on the validateStructTag code in package
    62  	// golang.org/x/tools/go/analysis/passes/structtag/structtag.go.
    63  	var st = StructTag{
    64  		tagsByKey: map[string]SubStructTag{},
    65  	}
    66  
    67  	// Code borrowed from golang.org/x/tools/go/analysis/passes/structtag/structtag.go
    68  	// Code borrowed from reflect/type.go
    69  	n := 0
    70  	for ; tag != ""; n++ {
    71  		if n > 0 && tag != "" && tag[0] != ' ' {
    72  			// More restrictive than reflect, but catches likely mistakes
    73  			// like `x:"foo",y:"bar"`, which parses as `x:"foo" ,y:"bar"` with second key ",y".
    74  			return nil, errTagSpace
    75  		}
    76  		// Skip leading space.
    77  		i := 0
    78  		for i < len(tag) && tag[i] == ' ' {
    79  			i++
    80  		}
    81  		tag = tag[i:]
    82  		if tag == "" {
    83  			break
    84  		}
    85  
    86  		// Scan to colon. A space, a quote or a control character is a syntax error.
    87  		// Strictly speaking, control chars include the range [0x7f, 0x9f], not just
    88  		// [0x00, 0x1f], but in practice, we ignore the multi-byte control characters
    89  		// as it is simpler to inspect the tag's bytes than the tag's runes.
    90  		i = 0
    91  		for i < len(tag) && tag[i] > ' ' && tag[i] != ':' && tag[i] != '"' && tag[i] != 0x7f {
    92  			i++
    93  		}
    94  		if i == 0 {
    95  			return nil, errTagKeySyntax
    96  		}
    97  		if i+1 >= len(tag) || tag[i] != ':' {
    98  			return nil, errTagSyntax
    99  		}
   100  		if tag[i+1] != '"' {
   101  			return nil, errTagValueSyntax
   102  		}
   103  		key := tag[:i]
   104  		if !IsValidTagKey(key) {
   105  			return nil, errTagKeySyntax
   106  		}
   107  		tag = tag[i+1:]
   108  
   109  		// Scan quoted string to find value.
   110  		i = 1
   111  		for i < len(tag) && tag[i] != '"' {
   112  			if tag[i] == '\\' {
   113  				i++
   114  			}
   115  			i++
   116  		}
   117  		if i >= len(tag) {
   118  			return nil, errTagValueSyntax
   119  		}
   120  		qvalue := tag[:i+1]
   121  		tag = tag[i+1:]
   122  
   123  		value, err := strconv.Unquote(qvalue)
   124  		if err != nil {
   125  			return nil, errTagValueSyntax
   126  		}
   127  
   128  		if checkTagSpaces[key] {
   129  			switch key {
   130  			case "xml":
   131  				// If the first or last character in the XML tag is a space, it is
   132  				// suspicious.
   133  				if strings.Trim(value, " ") != value {
   134  					return nil, errTagValueSpace
   135  				}
   136  
   137  				// If there are multiple spaces, they are suspicious.
   138  				if strings.Count(value, " ") > 1 {
   139  					return nil, errTagValueSpace
   140  				}
   141  
   142  				// If there is no comma, skip the rest of the checks.
   143  				comma := strings.IndexRune(value, ',')
   144  				if comma < 0 {
   145  					break
   146  				}
   147  
   148  				// If the character before a comma is a space, this is suspicious.
   149  				if comma > 0 && value[comma-1] == ' ' {
   150  					return nil, errTagValueSpace
   151  				}
   152  				//value = value[comma+1:]
   153  			case "json":
   154  				// JSON allows using spaces in the name, so skip it.
   155  				comma := strings.IndexRune(value, ',')
   156  				if comma < 0 {
   157  					break
   158  				}
   159  
   160  				//value = value[comma+1:]
   161  			}
   162  
   163  			// If spaces exists in tag's value, it is suspicious.
   164  			if strings.IndexByte(value, ' ') >= 0 {
   165  				return nil, errTagValueSpace
   166  			}
   167  		}
   168  
   169  		res := strings.Split(value, ",")
   170  		name := res[0]
   171  		options := res[1:]
   172  		if len(options) == 0 {
   173  			options = nil
   174  		}
   175  
   176  		if _, has := st.tagsByKey[key]; has {
   177  			return nil, errTagDuplicatedKey
   178  		}
   179  		st.orderedKeys = append(st.orderedKeys, key)
   180  		st.tagsByKey[key] = SubStructTag{
   181  			Key:     key,
   182  			Name:    name,
   183  			Options: options,
   184  		}
   185  	}
   186  
   187  	return &st, nil
   188  }
   189  
   190  // Get returns the tag associated with the given key. If the key is present
   191  // in the tag the value (which may be empty) is returned. Otherwise the
   192  // returned value will be the empty string. The ok return value reports whether
   193  // the tag exists or not (which the return value is nil).
   194  func (t StructTag) Get(key string) (tag SubStructTag, ok bool) {
   195  	if t.orderedKeys == nil {
   196  		return SubStructTag{}, false
   197  	}
   198  	tag, ok = t.tagsByKey[key]
   199  	return
   200  }
   201  
   202  func (t *StructTag) appendOrderedKeysIfNotPresent(key string) {
   203  	if t.tagsByKey == nil {
   204  		t.tagsByKey = make(map[string]SubStructTag)
   205  	}
   206  	if _, has := t.tagsByKey[key]; !has {
   207  		t.orderedKeys = append(t.orderedKeys, key)
   208  	}
   209  }
   210  
   211  // Set sets the given tag. If the tag key already exists it'll override it
   212  func (t *StructTag) Set(subTag SubStructTag) error {
   213  	if subTag.Key == "" {
   214  		return errKeyNotSet
   215  	}
   216  	t.appendOrderedKeysIfNotPresent(subTag.Key)
   217  	t.tagsByKey[subTag.Key] = subTag
   218  	return nil
   219  }
   220  
   221  // SetName sets the given name for the given key.
   222  func (t *StructTag) SetName(key string, name string) {
   223  	t.appendOrderedKeysIfNotPresent(key)
   224  
   225  	val, _ := t.tagsByKey[key]
   226  	val.Key = key
   227  	val.Name = name
   228  	t.tagsByKey[key] = val
   229  }
   230  
   231  // AddOptions adds the given option for the given key.
   232  // It appends to any existing options associated with key.
   233  func (t *StructTag) AddOptions(key string, options ...string) {
   234  	t.appendOrderedKeysIfNotPresent(key)
   235  
   236  	val, _ := t.tagsByKey[key]
   237  	val.Key = key
   238  	val.AddOptions(options...)
   239  	t.tagsByKey[key] = val
   240  }
   241  
   242  // DeleteOptions deletes the given options for the given key
   243  func (t *StructTag) DeleteOptions(key string, options ...string) {
   244  	if t.tagsByKey == nil {
   245  		return
   246  	}
   247  
   248  	val, has := t.tagsByKey[key]
   249  	if !has {
   250  		return
   251  	}
   252  	val.DeleteOptions(options...)
   253  	t.tagsByKey[key] = val
   254  }
   255  
   256  // Delete deletes the tag for the given keys
   257  func (t *StructTag) Delete(keys ...string) {
   258  	if t.tagsByKey == nil {
   259  		return
   260  	}
   261  
   262  	for _, key := range keys {
   263  		delete(t.tagsByKey, key)
   264  		strings_.SliceTrim(t.orderedKeys, key)
   265  	}
   266  }
   267  
   268  // Keys returns a slice of subTags.
   269  func (t StructTag) Keys() []string {
   270  	var keys []string
   271  	for key := range t.tagsByKey {
   272  		keys = append(keys, key)
   273  	}
   274  	return keys
   275  }
   276  
   277  // SortedKeys returns a slice of subTags sorted by keys in increasing order.
   278  func (t StructTag) SortedKeys() []string {
   279  	keys := t.Keys()
   280  	sort.Strings(keys)
   281  	return keys
   282  }
   283  
   284  // OrderKeys returns a slice of subTags with original order.
   285  func (t StructTag) OrderKeys() []string {
   286  	return t.orderedKeys
   287  }
   288  
   289  // SelectedTags returns a slice of subTags in keys order.
   290  func (t StructTag) SelectedTags(keys ...string) []SubStructTag {
   291  	if len(keys) == 0 {
   292  		return nil
   293  	}
   294  	var tags []SubStructTag
   295  	for _, key := range keys {
   296  		tag, has := t.tagsByKey[key]
   297  		if !has {
   298  			continue
   299  		}
   300  		tags = append(tags, tag)
   301  	}
   302  	return tags
   303  }
   304  
   305  // SelectString reassembles the subTags selected by keys into a valid literal tag field representation
   306  // tag json:"name,omitempty", reflect.StructField.Tag returned by reflect
   307  func (t StructTag) SelectString(keys ...string) string {
   308  	tags := t.SelectedTags(keys...)
   309  	if len(tags) == 0 {
   310  		return ""
   311  	}
   312  
   313  	var buf bytes.Buffer
   314  	for i, tag := range tags {
   315  		buf.WriteString(tag.String())
   316  		if i != len(tags)-1 {
   317  			buf.WriteString(" ")
   318  		}
   319  	}
   320  	return buf.String()
   321  }
   322  
   323  // SelectAstString reassembles the subTags selected by keys into a valid literal tag field representation
   324  // tag json:"name,omitempty", reflect.StructField.Tag returned by reflect
   325  func (t StructTag) SelectAstString(keys ...string) string {
   326  	tag := t.SelectString(keys...)
   327  	if tag == "" {
   328  		return tag
   329  	}
   330  	return "`" + tag + "`"
   331  }
   332  
   333  // Tags returns a slice of subTags with original order.
   334  func (t StructTag) Tags() []SubStructTag {
   335  	return t.SelectedTags(t.Keys()...)
   336  }
   337  
   338  // OrderedTags returns a slice of subTags sorted by keys in increasing order.
   339  func (t StructTag) SortedTags() []SubStructTag {
   340  	return t.SelectedTags(t.SortedKeys()...)
   341  }
   342  
   343  // Tags returns a slice of subTags with original order.
   344  func (t StructTag) OrderedTags() []SubStructTag {
   345  	return t.SelectedTags(t.OrderKeys()...)
   346  }
   347  
   348  // String reassembles the subTags into a valid literal tag field representation
   349  // key is random.
   350  // tag json:"name,omitempty", reflect.StructField.Tag returned by reflect
   351  func (t StructTag) String() string {
   352  	return t.SelectString(t.Keys()...)
   353  }
   354  
   355  // SortedString reassembles the subTags into a valid literal tag field representation
   356  // key is sorted by keys in increasing order.
   357  // tag json:"name,omitempty", reflect.StructField.Tag returned by reflect
   358  func (t StructTag) SortedString() string {
   359  	return t.SelectString(t.SortedKeys()...)
   360  }
   361  
   362  // OrderedString reassembles the subTags into a valid literal tag field representation
   363  // key is in the original order.
   364  // tag json:"name,omitempty", reflect.StructField.Tag returned by reflect
   365  func (t StructTag) OrderedString() string {
   366  	return t.SelectString(t.OrderKeys()...)
   367  }
   368  
   369  // AstString reassembles the subTags into a valid literal ast tag field representation
   370  // key is random.
   371  // tag `json:"name,omitempty"`, field.Tag.Value returned by AST
   372  func (t StructTag) AstString() string {
   373  	return t.SelectAstString(t.Keys()...)
   374  }
   375  
   376  // SortedAstString reassembles the subTags into a valid literal ast tag field representation
   377  // key is sorted by keys in increasing order.
   378  // tag `json:"name,omitempty"`, field.Tag.Value returned by AST
   379  func (t StructTag) SortedAstString() string {
   380  	return t.SelectAstString(t.SortedKeys()...)
   381  }
   382  
   383  // OrderedAstString reassembles the subTags into a valid literal ast tag field representation
   384  // key is in the original order.
   385  // tag `json:"name,omitempty"`, field.Tag.Value returned by AST
   386  func (t StructTag) OrderedAstString() string {
   387  	return t.SelectAstString(t.OrderKeys()...)
   388  }
   389  
   390  var checkTagSpaces = map[string]bool{"json": true, "xml": true, "asn1": true}