github.com/aacfactory/avro@v1.2.12/internal/base/schema_value.go (about)

     1  package base
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/modern-go/reflect2"
     6  	"reflect"
     7  	"strings"
     8  )
     9  
    10  func RegisterSchemaByValue(v any) {
    11  	_, err := ParseValue(v)
    12  	if err != nil {
    13  		panic(err)
    14  	}
    15  }
    16  
    17  func ParseValue(v any) (s Schema, err error) {
    18  	typ := reflect2.TypeOf(v)
    19  	if typ.Kind() == reflect.Ptr {
    20  		typ = typ.(reflect2.PtrType).Elem()
    21  	}
    22  	key := makeSchemaName(typ)
    23  	if key == "" {
    24  		err = fmt.Errorf("avro: type %s is unsupported", typ.String())
    25  		return
    26  	}
    27  	s = DefaultSchemaCache.Get(key)
    28  	if s != nil {
    29  		return
    30  	}
    31  	r, doErr, _ := DefaultSchemaCache.processingGroup.Do(key, func() (r any, err error) {
    32  		parsed, parseErr := parseValueType(typ)
    33  		if parseErr != nil {
    34  			err = parseErr
    35  			return
    36  		}
    37  		DefaultSchemaCache.Add(key, parsed)
    38  		r = parsed
    39  		return
    40  	})
    41  	if doErr != nil {
    42  		err = doErr
    43  		return
    44  	}
    45  	s = r.(Schema)
    46  	return
    47  }
    48  
    49  func tryParseMarshal(typ reflect2.Type) (s Schema) {
    50  	if typ.Implements(marshalerType) || typ.Implements(unmarshalerType) {
    51  		return NewPrimitiveSchema(Raw, nil)
    52  	}
    53  	if reflect2.PtrTo(typ).Implements(unmarshalerType) {
    54  		return NewPrimitiveSchema(Raw, nil)
    55  	}
    56  	if reflect2.PtrTo(typ).Implements(textMarshalerType) {
    57  		return NewPrimitiveSchema(String, nil)
    58  	}
    59  	if typ.Implements(textMarshalerType) {
    60  		return NewPrimitiveSchema(String, nil)
    61  	}
    62  	if reflect2.PtrTo(typ).Implements(textUnmarshalerType) {
    63  		return NewPrimitiveSchema(String, nil)
    64  	}
    65  	if typ.Implements(textUnmarshalerType) {
    66  		return NewPrimitiveSchema(String, nil)
    67  	}
    68  	return nil
    69  }
    70  
    71  func parseValueType(typ reflect2.Type) (s Schema, err error) {
    72  	ms := tryParseMarshal(typ)
    73  	if ms != nil {
    74  		s = ms
    75  		return
    76  	}
    77  	switch typ.Kind() {
    78  	case reflect.String:
    79  		return NewPrimitiveSchema(String, nil), nil
    80  	case reflect.Bool:
    81  		return NewPrimitiveSchema(Boolean, nil), nil
    82  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint8, reflect.Uint16:
    83  		return NewPrimitiveSchema(Int, nil), nil
    84  	case reflect.Int64:
    85  		if typ.Type1().ConvertibleTo(timeType) {
    86  			return NewPrimitiveSchema(Long, NewPrimitiveLogicalSchema(Duration)), nil
    87  		}
    88  		return NewPrimitiveSchema(Long, nil), nil
    89  	case reflect.Uint32:
    90  		return NewPrimitiveSchema(Long, nil), nil
    91  	case reflect.Float32:
    92  		return NewPrimitiveSchema(Float, nil), nil
    93  	case reflect.Float64:
    94  		return NewPrimitiveSchema(Double, nil), nil
    95  	case reflect.Uint, reflect.Uint64:
    96  		return NewFixedSchema("uint", "", 8, NewPrimitiveLogicalSchema(Decimal))
    97  	case reflect.Struct:
    98  		return parseStructType(typ)
    99  	case reflect.Ptr:
   100  		return parsePtrType(typ)
   101  	case reflect.Slice:
   102  		return parseSliceType(typ)
   103  	case reflect.Array:
   104  		return parseArrayType(typ)
   105  	case reflect.Map:
   106  		return parseMapType(typ)
   107  	default:
   108  		if typ.Implements(marshalerType) || typ.Implements(unmarshalerType) {
   109  			return NewPrimitiveSchema(Raw, nil), nil
   110  		}
   111  		if reflect2.PtrTo(typ).Implements(unmarshalerType) {
   112  			return NewPrimitiveSchema(Raw, nil), nil
   113  		}
   114  		return nil, fmt.Errorf("avro: type %s is unsupported", typ.String())
   115  	}
   116  }
   117  
   118  func makeSchemaName(typ reflect2.Type) string {
   119  	if typ.Implements(marshalerType) || typ.Implements(unmarshalerType) {
   120  		return namespace(typ.Type1().PkgPath()) + "." + typ.Type1().Name()
   121  	}
   122  	if reflect2.PtrTo(typ).Implements(unmarshalerType) {
   123  		return namespace(typ.Type1().PkgPath()) + "." + typ.Type1().Name()
   124  	}
   125  	switch typ.Kind() {
   126  	case reflect.String:
   127  		return string(String)
   128  	case reflect.Bool:
   129  		return string(Boolean)
   130  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint8, reflect.Uint16:
   131  		return string(Int)
   132  	case reflect.Int64:
   133  		if typ.Type1().PkgPath() == "time" && typ.Type1().Name() == "Duration" {
   134  			return string(Long) + "." + string(Duration)
   135  		}
   136  		return string(Long)
   137  	case reflect.Uint32:
   138  		return string(Long)
   139  	case reflect.Float32:
   140  		return string(Float)
   141  	case reflect.Float64:
   142  		return string(Double)
   143  	case reflect.Uint, reflect.Uint64:
   144  		return string(Fixed) + "." + string(Decimal)
   145  	case reflect.Struct:
   146  		if typ.Type1().ConvertibleTo(timeType) {
   147  			return string(Long) + "." + string(TimestampMicros)
   148  		}
   149  		return namespace(typ.Type1().PkgPath()) + "." + typ.Type1().Name()
   150  	case reflect.Ptr:
   151  		elemType := typ.Type1().Elem()
   152  		if elemType.Kind() != reflect.Struct {
   153  			return ""
   154  		}
   155  		elem := makeSchemaName(reflect2.Type2(elemType))
   156  		if elem == "" {
   157  			return ""
   158  		}
   159  		return elem + "_ptr"
   160  	case reflect.Slice:
   161  		if typ.Type1().Elem().Kind() == reflect.Uint8 {
   162  			return string(Bytes)
   163  		}
   164  		elem := makeSchemaName(typ.(reflect2.SliceType).Elem())
   165  		if elem == "" {
   166  			return ""
   167  		}
   168  		return elem + "_slice"
   169  	case reflect.Array:
   170  		elem := makeSchemaName(typ.(reflect2.ArrayType).Elem())
   171  		if elem == "" {
   172  			return ""
   173  		}
   174  		return elem + "_array"
   175  	case reflect.Map:
   176  		mapType := typ.(reflect2.MapType)
   177  		if mapType.Key().Kind() != reflect.String {
   178  			return ""
   179  		}
   180  		elem := makeSchemaName(mapType.Elem())
   181  		if elem == "" {
   182  			return ""
   183  		}
   184  		return elem + "_map"
   185  	default:
   186  		return ""
   187  	}
   188  }
   189  
   190  func namespace(pkg string) (v string) {
   191  	v = strings.ReplaceAll(pkg, "/", ".")
   192  	v = strings.ReplaceAll(v, "-", "_")
   193  	return
   194  }