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  }