1package runtime
2
3import (
4	"reflect"
5	"strings"
6	"unicode"
7)
8
9func getTag(field reflect.StructField) string {
10	return field.Tag.Get("json")
11}
12
13func IsIgnoredStructField(field reflect.StructField) bool {
14	if field.PkgPath != "" {
15		if field.Anonymous {
16			if !(field.Type.Kind() == reflect.Ptr && field.Type.Elem().Kind() == reflect.Struct) && field.Type.Kind() != reflect.Struct {
17				return true
18			}
19		} else {
20			// private field
21			return true
22		}
23	}
24	tag := getTag(field)
25	return tag == "-"
26}
27
28type StructTag struct {
29	Key         string
30	IsTaggedKey bool
31	IsOmitEmpty bool
32	IsString    bool
33	Field       reflect.StructField
34}
35
36type StructTags []*StructTag
37
38func (t StructTags) ExistsKey(key string) bool {
39	for _, tt := range t {
40		if tt.Key == key {
41			return true
42		}
43	}
44	return false
45}
46
47func isValidTag(s string) bool {
48	if s == "" {
49		return false
50	}
51	for _, c := range s {
52		switch {
53		case strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~ ", c):
54			// Backslash and quote chars are reserved, but
55			// otherwise any punctuation chars are allowed
56			// in a tag name.
57		case !unicode.IsLetter(c) && !unicode.IsDigit(c):
58			return false
59		}
60	}
61	return true
62}
63
64func StructTagFromField(field reflect.StructField) *StructTag {
65	keyName := field.Name
66	tag := getTag(field)
67	st := &StructTag{Field: field}
68	opts := strings.Split(tag, ",")
69	if len(opts) > 0 {
70		if opts[0] != "" && isValidTag(opts[0]) {
71			keyName = opts[0]
72			st.IsTaggedKey = true
73		}
74	}
75	st.Key = keyName
76	if len(opts) > 1 {
77		for _, opt := range opts[1:] {
78			switch opt {
79			case "omitempty":
80				st.IsOmitEmpty = true
81			case "string":
82				st.IsString = true
83			}
84		}
85	}
86	return st
87}
88