func convertToBaseType(value reflect.Value) reflect.Value {
	return value.Convert(getBaseType(value.Type()))
}
convertToBaseType takes a value which have a custom type (type MyType string) and returns the translation in basic type
package avro
import (
	"encoding/json"
	"reflect"
	"strings"
	"github.com/fatih/camelcase"
	"github.com/leboncoin/structs"
	"github.com/linkedin/goavro/v2"
	"github.com/mitchellh/mapstructure"
)
type TypeNameEncoder func(name string) string
func CamelCaseToSnakeCase(name string) string {
	result := camelcase.Split(name)
	for i, s := range result {
		result[i] = strings.ToLower(s)
	}
	return strings.Join(result, "_")
}
var typeConversion = map[string]string{
	"uint8":   "int",
	"uint16":  "int",
	"uint32":  "int",
	"int8":    "int",
	"int16":   "int",
	"int32":   "int",
	"rune":    "int",
	"uint":    "long",
	"int":     "long",
	"uint64":  "long",
	"int64":   "long",
	"float32": "float",
	"float64": "double",
	"bool":    "boolean",
}
var avroBaseTypes = []string{
	"null",
	"boolean",
	"int",
	"long",
	"float",
	"double",
	"bytes",
	"string",
}
func GoToAvroType(name string) string {
	out, ok := typeConversion[name]
	if !ok {
		return name
	}
	return out
}
func DefaultTypeNameEncoder(name string) string {
	return CamelCaseToSnakeCase(GoToAvroType(name))
}
func AddNamespace(namespace, typeName string) string {
	if len(namespace) == 0 {
		return typeName
	}
	return namespace + "." + typeName
}
type avroSchemaNamespace struct {
	Namespace string `json:"namespace,omitempty"`
}
type TypeNamer interface {
	AvroName() string
}
type CustomUnmarshaler interface {
	UnmarshalAvro(b []byte) error
}
type Marshaler interface {
	Marshal(interface{}) ([]byte, error)
}
type Unmarshaler interface {
	Unmarshal([]byte, interface{}) error
}
type Codec struct {
	goavro.Codec
	Namespace string
	TypeNameEncoder TypeNameEncoder
}
func NewCodec(schemaSpecification string) (*Codec, error) {
	o, err := goavro.NewCodec(schemaSpecification)
	if err != nil {
		return nil, err
	}
	namespaceStruct := avroSchemaNamespace{}
	err = json.Unmarshal([]byte(schemaSpecification), &namespaceStruct)
	var namespace string
	if err != nil {
		namespace = ""
	} else {
		namespace = namespaceStruct.Namespace
	}
	return &Codec{*o, namespace, DefaultTypeNameEncoder}, nil
}
func (c *Codec) Marshal(st interface{}) ([]byte, error) {
	return c.marshal(&c.Codec, st)
}
func (c *Codec) Unmarshal(avro []byte, output interface{}) error {
	return c.unmarshal(&c.Codec, avro, output)
}
func (c *Codec) addNamespace(typeName string) string {
	return AddNamespace(c.Namespace, typeName)
}
func (c *Codec) encodeTypeName(name string) string {
	return c.TypeNameEncoder(name)
}
func (c *Codec) encodeUnionHook(kind reflect.Kind, data interface{}) (interface{}, error) {
	value := reflect.ValueOf(data)
	switch kind {
	case reflect.Struct:
		s := structs.New(data)
		s.TagName = "avro"
		s.EncodeHook = c.encodeUnionHook
		data = s.Map()
	case reflect.Slice:
		elemType := getBaseType(reflect.TypeOf(data).Elem())
		newData := reflect.MakeSlice(reflect.SliceOf(elemType), value.Len(), value.Cap())
		for idx := 0; idx < value.Len(); idx++ {
			elem := value.Index(idx)
			val, err := c.encodeUnionHook(elem.Kind(), elem.Interface())
			if err != nil {
				return nil, err
			}
			newData.Index(idx).Set(reflect.ValueOf(val))
		}
		data = newData.Interface()
	case reflect.Ptr:
		if data != nil {
			pointed := value.Elem()
			if pointed.IsValid() {
				val, err := c.encodeUnionHook(pointed.Kind(), pointed.Interface())
				if err != nil {
					return nil, err
				}
				typeName := c.getTypeName(pointed)
				if !isAvroBaseType(typeName) {
					typeName = c.addNamespace(typeName)
				}
				data = map[string]interface{}{
					typeName: val,
				}
			} else {
				data = map[string]interface{}{
					"null": nil,
				}
			}
		}
	default:
		data = convertToBaseType(value).Interface()
	}
	return data, nil
}
func isAvroBaseType(avroType string) bool {
	for _, t := range avroBaseTypes {
		if t == avroType {
			return true
		}
	}
	return false
}
func getBaseType(t reflect.Type) reflect.Type {
	switch t.Kind() {
	case reflect.String:
		return reflect.TypeOf("")
	case reflect.Uint:
		return reflect.TypeOf(uint(0))
	case reflect.Uint8:
		return reflect.TypeOf(uint8(0))
	case reflect.Uint16:
		return reflect.TypeOf(uint16(0))
	case reflect.Uint32:
		return reflect.TypeOf(uint32(0))
	case reflect.Uint64:
		return reflect.TypeOf(uint64(0))
	case reflect.Int:
		return reflect.TypeOf(int(0))
	case reflect.Int8:
		return reflect.TypeOf(int8(0))
	case reflect.Int16:
		return reflect.TypeOf(int16(0))
	case reflect.Int32:
		return reflect.TypeOf(int32(0))
	case reflect.Int64:
		return reflect.TypeOf(int64(0))
	case reflect.Float32:
		return reflect.TypeOf(float32(0))
	case reflect.Float64:
		return reflect.TypeOf(float64(0))
	case reflect.Struct, reflect.Ptr:
		return reflect.TypeOf(map[string]interface{}{})
	case reflect.Slice:
		elemType := getBaseType(t.Elem())
		return reflect.SliceOf(elemType)
	default:
		return t
	}
}