File: struct.go

package info (click to toggle)
golang-github-segmentio-encoding 0.5.3-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 14,468 kB
  • sloc: makefile: 286
file content (140 lines) | stat: -rw-r--r-- 3,275 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package thrift

import (
	"fmt"
	"reflect"
	"strconv"
	"strings"
)

type flags int16

const (
	enum     flags = 1 << 0
	union    flags = 1 << 1
	required flags = 1 << 2
	optional flags = 1 << 3
	strict   flags = 1 << 4

	featuresBitOffset  = 8
	useDeltaEncoding   = flags(UseDeltaEncoding) << featuresBitOffset
	coalesceBoolFields = flags(CoalesceBoolFields) << featuresBitOffset

	structFlags   flags = enum | union | required | optional
	encodeFlags   flags = strict | protocolFlags
	decodeFlags   flags = strict | protocolFlags
	protocolFlags flags = useDeltaEncoding | coalesceBoolFields
)

func (f flags) have(x flags) bool {
	return (f & x) == x
}

func (f flags) only(x flags) flags {
	return f & x
}

func (f flags) with(x flags) flags {
	return f | x
}

func (f flags) without(x flags) flags {
	return f & ^x
}

type structField struct {
	typ   reflect.Type
	index []int
	id    int16
	flags flags
}

func forEachStructField(t reflect.Type, index []int, do func(structField)) {
	for i := range t.NumField() {
		f := t.Field(i)

		if f.PkgPath != "" && !f.Anonymous { // unexported
			continue
		}

		fieldIndex := append(index, i)
		fieldIndex = fieldIndex[:len(fieldIndex):len(fieldIndex)]

		if f.Anonymous {
			fieldType := f.Type

			for fieldType.Kind() == reflect.Ptr {
				fieldType = fieldType.Elem()
			}

			if fieldType.Kind() == reflect.Struct {
				forEachStructField(fieldType, fieldIndex, do)
				continue
			}
		}

		tag := f.Tag.Get("thrift")
		if tag == "" {
			continue
		}
		tags := strings.Split(tag, ",")
		flags := flags(0)

		for _, opt := range tags[1:] {
			switch opt {
			case "enum":
				flags = flags.with(enum)
			case "union":
				flags = flags.with(union)
			case "required":
				flags = flags.with(required)
			case "optional":
				flags = flags.with(optional)
			default:
				panic(fmt.Errorf("thrift struct field contains an unknown tag option %q in `thrift:\"%s\"`", opt, tag))
			}
		}

		if flags.have(optional | required) {
			panic(fmt.Errorf("thrift struct field cannot be both optional and required in `thrift:\"%s\"`", tag))
		}

		if flags.have(union) {
			if f.Type.Kind() != reflect.Interface {
				panic(fmt.Errorf("thrift union tag found on a field which is not an interface type `thrift:\"%s\"`", tag))
			}

			if tags[0] != "" {
				panic(fmt.Errorf("invalid thrift field id on union field `thrift:\"%s\"`", tag))
			}

			do(structField{
				typ:   f.Type,
				index: fieldIndex,
				flags: flags,
			})
		} else {
			if flags.have(enum) {
				switch f.Type.Kind() {
				case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
				case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
				default:
					panic(fmt.Errorf("thrift enum tag found on a field which is not an integer type `thrift:\"%s\"`", tag))
				}
			}

			if id, err := strconv.ParseInt(tags[0], 10, 16); err != nil {
				panic(fmt.Errorf("invalid thrift field id found in struct tag `thrift:\"%s\"`: %w", tag, err))
			} else if id <= 0 {
				panic(fmt.Errorf("invalid thrift field id found in struct tag `thrift:\"%s\"`: %d <= 0", tag, id))
			} else {
				do(structField{
					typ:   f.Type,
					index: fieldIndex,
					id:    int16(id),
					flags: flags,
				})
			}
		}
	}
}