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 }