github.com/kamalshkeir/kencoding@v0.0.2-0.20230409043843-44b609a0475a/thrift/struct.go (about) 1 package thrift 2 3 import ( 4 "fmt" 5 "reflect" 6 "strconv" 7 "strings" 8 ) 9 10 type flags int16 11 12 const ( 13 enum flags = 1 << 0 14 union flags = 1 << 1 15 required flags = 1 << 2 16 optional flags = 1 << 3 17 strict flags = 1 << 4 18 19 featuresBitOffset = 8 20 useDeltaEncoding = flags(UseDeltaEncoding) << featuresBitOffset 21 coalesceBoolFields = flags(CoalesceBoolFields) << featuresBitOffset 22 23 structFlags flags = enum | union | required | optional 24 encodeFlags flags = strict | protocolFlags 25 decodeFlags flags = strict | protocolFlags 26 protocolFlags flags = useDeltaEncoding | coalesceBoolFields 27 ) 28 29 func (f flags) have(x flags) bool { 30 return (f & x) == x 31 } 32 33 func (f flags) only(x flags) flags { 34 return f & x 35 } 36 37 func (f flags) with(x flags) flags { 38 return f | x 39 } 40 41 func (f flags) without(x flags) flags { 42 return f & ^x 43 } 44 45 type structField struct { 46 typ reflect.Type 47 index []int 48 id int16 49 flags flags 50 } 51 52 func forEachStructField(t reflect.Type, index []int, do func(structField)) { 53 for i, n := 0, t.NumField(); i < n; i++ { 54 f := t.Field(i) 55 56 if f.PkgPath != "" && !f.Anonymous { // unexported 57 continue 58 } 59 60 fieldIndex := append(index, i) 61 fieldIndex = fieldIndex[:len(fieldIndex):len(fieldIndex)] 62 63 if f.Anonymous { 64 fieldType := f.Type 65 66 for fieldType.Kind() == reflect.Ptr { 67 fieldType = fieldType.Elem() 68 } 69 70 if fieldType.Kind() == reflect.Struct { 71 forEachStructField(fieldType, fieldIndex, do) 72 continue 73 } 74 } 75 76 tag := f.Tag.Get("thrift") 77 if tag == "" { 78 continue 79 } 80 tags := strings.Split(tag, ",") 81 flags := flags(0) 82 83 for _, opt := range tags[1:] { 84 switch opt { 85 case "enum": 86 flags = flags.with(enum) 87 case "union": 88 flags = flags.with(union) 89 case "required": 90 flags = flags.with(required) 91 case "optional": 92 flags = flags.with(optional) 93 default: 94 panic(fmt.Errorf("thrift struct field contains an unknown tag option %q in `thrift:\"%s\"`", opt, tag)) 95 } 96 } 97 98 if flags.have(optional | required) { 99 panic(fmt.Errorf("thrift struct field cannot be both optional and required in `thrift:\"%s\"`", tag)) 100 } 101 102 if flags.have(union) { 103 if f.Type.Kind() != reflect.Interface { 104 panic(fmt.Errorf("thrift union tag found on a field which is not an interface type `thrift:\"%s\"`", tag)) 105 } 106 107 if tags[0] != "" { 108 panic(fmt.Errorf("invalid thrift field id on union field `thrift:\"%s\"`", tag)) 109 } 110 111 do(structField{ 112 typ: f.Type, 113 index: fieldIndex, 114 flags: flags, 115 }) 116 } else { 117 if flags.have(enum) { 118 switch f.Type.Kind() { 119 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 120 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 121 default: 122 panic(fmt.Errorf("thrift enum tag found on a field which is not an integer type `thrift:\"%s\"`", tag)) 123 } 124 } 125 126 if id, err := strconv.ParseInt(tags[0], 10, 16); err != nil { 127 panic(fmt.Errorf("invalid thrift field id found in struct tag `thrift:\"%s\"`: %w", tag, err)) 128 } else if id <= 0 { 129 panic(fmt.Errorf("invalid thrift field id found in struct tag `thrift:\"%s\"`: %d <= 0", tag, id)) 130 } else { 131 do(structField{ 132 typ: f.Type, 133 index: fieldIndex, 134 id: int16(id), 135 flags: flags, 136 }) 137 } 138 } 139 } 140 }