github.com/aacfactory/avro@v1.2.12/internal/base/schema_struct.go (about) 1 package base 2 3 import ( 4 "fmt" 5 "github.com/modern-go/reflect2" 6 "reflect" 7 "strings" 8 ) 9 10 const ( 11 tag = "avro" 12 ) 13 14 func parseStructType(typ reflect2.Type) (s Schema, err error) { 15 if typ.Type1().ConvertibleTo(timeType) { 16 return NewPrimitiveSchema(Long, NewPrimitiveLogicalSchema(TimestampMicros)), nil 17 } 18 if typ.Implements(marshalerType) || typ.Implements(unmarshalerType) { 19 return NewPrimitiveSchema(Raw, nil), nil 20 } 21 pkg := typ.Type1().PkgPath() 22 pkg = namespace(pkg) 23 typeName := typ.Type1().Name() 24 processingKey := pkg + "." + typeName 25 s = DefaultSchemaCache.getProcessing(processingKey) 26 if s != nil { 27 return 28 } 29 rs, rsErr := NewRecordSchema(typeName, pkg, nil) 30 if rsErr != nil { 31 err = rsErr 32 return 33 } 34 DefaultSchemaCache.addProcessing(processingKey, rs) 35 36 fields, fieldsErr := parseStructFieldTypes(typ) 37 if fieldsErr != nil { 38 err = fieldsErr 39 return 40 } 41 rs.fields = fields 42 s = rs 43 return 44 } 45 46 func parseStructFieldTypes(typ reflect2.Type) (fields []*Field, err error) { 47 st := typ.(reflect2.StructType) 48 num := st.NumField() 49 for i := 0; i < num; i++ { 50 ft := st.Field(i).(*reflect2.UnsafeStructField) 51 if ft.Anonymous() { 52 if ft.Type().Kind() == reflect.Ptr && !ft.IsExported() { 53 continue 54 } 55 sub, subErr := parseStructFieldTypes(ft.Type()) 56 if subErr != nil { 57 err = subErr 58 return 59 } 60 fields = append(fields, sub...) 61 continue 62 } 63 if !ft.IsExported() { 64 continue 65 } 66 pname := strings.TrimSpace(ft.Tag().Get(tag)) 67 if pname == "-" { 68 continue 69 } 70 if pname == "" { 71 pname = ft.Name() 72 } 73 var field *Field 74 var fieldErr error 75 switch ft.Type().Kind() { 76 case reflect.String: 77 field, fieldErr = NewField(pname, NewPrimitiveSchema(String, nil)) 78 break 79 case reflect.Bool: 80 field, fieldErr = NewField(pname, NewPrimitiveSchema(Boolean, nil)) 81 break 82 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint8, reflect.Uint16: 83 field, fieldErr = NewField(pname, NewPrimitiveSchema(Int, nil)) 84 break 85 case reflect.Int64: 86 var fs Schema 87 if typ.Type1().ConvertibleTo(timeType) { 88 fs = NewPrimitiveSchema(Long, NewPrimitiveLogicalSchema(Duration)) 89 } else { 90 fs = NewPrimitiveSchema(Long, nil) 91 } 92 field, fieldErr = NewField(pname, fs) 93 break 94 case reflect.Uint32: 95 field, fieldErr = NewField(pname, NewPrimitiveSchema(Long, nil)) 96 break 97 case reflect.Float32: 98 field, fieldErr = NewField(pname, NewPrimitiveSchema(Float, nil)) 99 break 100 case reflect.Float64: 101 field, fieldErr = NewField(pname, NewPrimitiveSchema(Double, nil)) 102 break 103 case reflect.Uint, reflect.Uint64: 104 fs, fsErr := NewFixedSchema("uint", "", 8, NewPrimitiveLogicalSchema(Decimal)) 105 if fsErr != nil { 106 err = fmt.Errorf("avro: parse %s.%s failed, %v", st.String(), ft.Name(), fsErr) 107 return 108 } 109 field, fieldErr = NewField(pname, fs) 110 break 111 case reflect.Struct: 112 if typ.RType() == ft.Type().RType() { 113 err = fmt.Errorf("avro: parse %s.%s failed, %v", st.String(), ft.Name(), fmt.Errorf("please use ptr")) 114 return 115 } 116 ms := tryParseMarshal(ft.Type()) 117 if ms != nil { 118 field, fieldErr = NewField(pname, ms) 119 break 120 } 121 pkey := makeSchemaName(ft.Type()) 122 if pkey == "" { 123 err = fmt.Errorf("avro: parse %s.%s failed, %v", st.String(), ft.Name(), fmt.Errorf("unsupported type")) 124 return 125 } 126 127 processing := DefaultSchemaCache.getProcessing(pkey) 128 if processing != nil { 129 named, isName := processing.(NamedSchema) 130 if !isName { 131 err = fmt.Errorf("avro: parse %s.%s failed, %v", st.String(), ft.Name(), fmt.Errorf("unsupported type")) 132 return 133 } 134 field, fieldErr = NewField(pname, NewRefSchema(named)) 135 break 136 } 137 processing, err = parseValueType(ft.Type()) 138 if err != nil { 139 err = fmt.Errorf("avro: parse %s.%s failed, %v", st.String(), ft.Name(), err) 140 return 141 } 142 field, fieldErr = NewField(pname, processing) 143 break 144 case reflect.Ptr: 145 ptrType := ft.Type().(reflect2.PtrType) 146 ms := tryParseMarshal(ptrType) 147 if ms != nil { 148 field, fieldErr = NewField(pname, ms) 149 break 150 } 151 elemType := ptrType.Elem() 152 if elemType.Kind() != reflect.Struct { 153 err = fmt.Errorf("avro: parse %s.%s failed, %v", st.String(), ft.Name(), fmt.Errorf("unsupported type")) 154 return 155 } 156 pkey := makeSchemaName(elemType) 157 if pkey == "" { 158 err = fmt.Errorf("avro: parse %s.%s failed, %v", st.String(), ft.Name(), fmt.Errorf("unsupported type")) 159 return 160 } 161 processing := DefaultSchemaCache.getProcessing(pkey) 162 if processing != nil { 163 named, isName := processing.(NamedSchema) 164 if !isName { 165 err = fmt.Errorf("avro: parse %s.%s failed, %v", st.String(), ft.Name(), fmt.Errorf("unsupported type")) 166 return 167 } 168 union, unionErr := NewUnionSchema([]Schema{&NullSchema{}, NewRefSchema(named)}) 169 if unionErr != nil { 170 err = fmt.Errorf("avro: parse %s.%s failed, %v", st.String(), ft.Name(), unionErr) 171 return 172 } 173 field, fieldErr = NewField(pname, union, WithDefault(nil)) 174 break 175 } 176 processing, err = parseValueType(elemType) 177 if err != nil { 178 err = fmt.Errorf("avro: parse %s.%s failed, %v", st.String(), ft.Name(), err) 179 return 180 } 181 union, unionErr := NewUnionSchema([]Schema{&NullSchema{}, processing}) 182 if unionErr != nil { 183 err = fmt.Errorf("avro: parse %s.%s failed, %v", st.String(), ft.Name(), unionErr) 184 return 185 } 186 field, fieldErr = NewField(pname, union, WithDefault(nil)) 187 break 188 case reflect.Slice: 189 fs, fsErr := parseValueType(ft.Type()) 190 if fsErr != nil { 191 err = fmt.Errorf("avro: parse %s.%s failed, %v", st.String(), ft.Name(), fsErr) 192 return 193 } 194 field, fieldErr = NewField(pname, fs) 195 break 196 case reflect.Array: 197 fs, fsErr := parseValueType(ft.Type()) 198 if fsErr != nil { 199 err = fmt.Errorf("avro: parse %s.%s failed, %v", st.String(), ft.Name(), fsErr) 200 return 201 } 202 field, fieldErr = NewField(pname, fs) 203 break 204 case reflect.Map: 205 fs, fsErr := parseValueType(ft.Type()) 206 if fsErr != nil { 207 err = fmt.Errorf("avro: parse %s.%s failed, %v", st.String(), ft.Name(), fsErr) 208 return 209 } 210 field, fieldErr = NewField(pname, fs) 211 break 212 default: 213 if ft.Type().Implements(marshalerType) || ft.Type().Implements(unmarshalerType) { 214 field, fieldErr = NewField(pname, rawSchema) 215 break 216 } 217 pft := reflect2.PtrTo(ft.Type()) 218 if pft.Implements(marshalerType) || pft.Implements(unmarshalerType) { 219 field, fieldErr = NewField(pname, rawSchema) 220 break 221 } 222 err = fmt.Errorf("avro: parse %s.%s failed, %v", st.String(), ft.Name(), fmt.Errorf("unsupported type")) 223 return 224 } 225 226 if fieldErr != nil { 227 err = fmt.Errorf("avro: parse %s.%s failed, %v", st.String(), ft.Name(), fieldErr) 228 return 229 } 230 for _, f := range fields { 231 if f.name == field.name { 232 err = fmt.Errorf("avro: parse %s.%s failed, %v", st.String(), ft.Name(), fmt.Errorf("tag name is duplicated")) 233 return 234 } 235 } 236 fields = append(fields, field) 237 } 238 return 239 }