func ParseSchema(rawSchema string) (Schema, error) {
	return ParseSchemaWithRegistry(rawSchema, make(map[string]Schema))
}
ParseSchema parses a given schema without provided schemas to reuse.
Equivalent to call ParseSchemaWithResistry(rawSchema, make(map[string]Schema))
May return an error if schema is not parsable or has insufficient information about any type.
package avro
import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"math"
	"reflect"
	"strings"
)
const (
	Record int = iota
	Enum
	Array
	Map
	Union
	Fixed
	String
	Bytes
	Int
	Long
	Float
	Double
	Boolean
	Null
	Recursive
)
const (
	typeRecord  = "record"
	typeUnion   = "union"
	typeEnum    = "enum"
	typeArray   = "array"
	typeMap     = "map"
	typeFixed   = "fixed"
	typeString  = "string"
	typeBytes   = "bytes"
	typeInt     = "int"
	typeLong    = "long"
	typeFloat   = "float"
	typeDouble  = "double"
	typeBoolean = "boolean"
	typeNull    = "null"
)
const (
	schemaAliasesField   = "aliases"
	schemaDefaultField   = "default"
	schemaDocField       = "doc"
	schemaFieldsField    = "fields"
	schemaItemsField     = "items"
	schemaNameField      = "name"
	schemaNamespaceField = "namespace"
	schemaSizeField      = "size"
	schemaSymbolsField   = "symbols"
	schemaTypeField      = "type"
	schemaValuesField    = "values"
)
type Schema interface {
	Type() int
	GetName() string
	Prop(key string) (interface{}, bool)
	String() string
	Validate(v reflect.Value) bool
}
type StringSchema struct{}
func (*StringSchema) String() string {
	return `{"type": "string"}`
}
func (*StringSchema) Type() int {
	return String
}
func (*StringSchema) GetName() string {
	return typeString
}
func (*StringSchema) Prop(key string) (interface{}, bool) {
	return nil, false
}
func (*StringSchema) Validate(v reflect.Value) bool {
	_, ok := dereference(v).Interface().(string)
	return ok
}
func (*StringSchema) MarshalJSON() ([]byte, error) {
	return []byte(`"string"`), nil
}
type BytesSchema struct{}
func (*BytesSchema) String() string {
	return `{"type": "bytes"}`
}
func (*BytesSchema) Type() int {
	return Bytes
}
func (*BytesSchema) GetName() string {
	return typeBytes
}
func (*BytesSchema) Prop(key string) (interface{}, bool) {
	return nil, false
}
func (*BytesSchema) Validate(v reflect.Value) bool {
	v = dereference(v)
	return v.Kind() == reflect.Slice && v.Type().Elem().Kind() == reflect.Uint8
}
func (*BytesSchema) MarshalJSON() ([]byte, error) {
	return []byte(`"bytes"`), nil
}
type IntSchema struct{}
func (*IntSchema) String() string {
	return `{"type": "int"}`
}
func (*IntSchema) Type() int {
	return Int
}
func (*IntSchema) GetName() string {
	return typeInt
}
func (*IntSchema) Prop(key string) (interface{}, bool) {
	return nil, false
}
func (*IntSchema) Validate(v reflect.Value) bool {
	return reflect.TypeOf(dereference(v).Interface()).Kind() == reflect.Int32
}
func (*IntSchema) MarshalJSON() ([]byte, error) {
	return []byte(`"int"`), nil
}
type LongSchema struct{}
func (*LongSchema) String() string {
	return `{"type": "long"}`
}
func (*LongSchema) Type() int {
	return Long
}
func (*LongSchema) GetName() string {
	return typeLong
}
func (*LongSchema) Prop(key string) (interface{}, bool) {
	return nil, false
}
func (*LongSchema) Validate(v reflect.Value) bool {
	return reflect.TypeOf(dereference(v).Interface()).Kind() == reflect.Int64
}
func (*LongSchema) MarshalJSON() ([]byte, error) {
	return []byte(`"long"`), nil
}
type FloatSchema struct{}
func (*FloatSchema) String() string {
	return `{"type": "float"}`
}
func (*FloatSchema) Type() int {
	return Float
}
func (*FloatSchema) GetName() string {
	return typeFloat
}
func (*FloatSchema) Prop(key string) (interface{}, bool) {
	return nil, false
}
func (*FloatSchema) Validate(v reflect.Value) bool {
	return reflect.TypeOf(dereference(v).Interface()).Kind() == reflect.Float32
}
func (*FloatSchema) MarshalJSON() ([]byte, error) {
	return []byte(`"float"`), nil
}
type DoubleSchema struct{}
func (*DoubleSchema) String() string {
	return `{"type": "double"}`
}
func (*DoubleSchema) Type() int {
	return Double
}
func (*DoubleSchema) GetName() string {
	return typeDouble
}
func (*DoubleSchema) Prop(key string) (interface{}, bool) {
	return nil, false
}
func (*DoubleSchema) Validate(v reflect.Value) bool {
	return reflect.TypeOf(dereference(v).Interface()).Kind() == reflect.Float64
}
func (*DoubleSchema) MarshalJSON() ([]byte, error) {
	return []byte(`"double"`), nil
}
type BooleanSchema struct{}
func (*BooleanSchema) String() string {
	return `{"type": "boolean"}`
}
func (*BooleanSchema) Type() int {
	return Boolean
}
func (*BooleanSchema) GetName() string {
	return typeBoolean
}
func (*BooleanSchema) Prop(key string) (interface{}, bool) {
	return nil, false
}
func (*BooleanSchema) Validate(v reflect.Value) bool {
	return reflect.TypeOf(dereference(v).Interface()).Kind() == reflect.Bool
}
func (*BooleanSchema) MarshalJSON() ([]byte, error) {
	return []byte(`"boolean"`), nil
}
type NullSchema struct{}
func (*NullSchema) String() string {
	return `{"type": "null"}`
}
func (*NullSchema) Type() int {
	return Null
}
func (*NullSchema) GetName() string {
	return typeNull
}
func (*NullSchema) Prop(key string) (interface{}, bool) {
	return nil, false
}
func (*NullSchema) Validate(v reflect.Value) bool {
	switch v.Kind() {
	case reflect.Interface:
		return v.IsNil()
	case reflect.Array:
		return v.Cap() == 0
	case reflect.Slice:
		return v.IsNil() || v.Cap() == 0
	case reflect.Map:
		return len(v.MapKeys()) == 0
	case reflect.String:
		return len(v.String()) == 0
	case reflect.Float32:
		return math.IsNaN(v.Float())
	case reflect.Float64:
		return math.IsNaN(v.Float())
	case reflect.Ptr:
		return v.IsNil()
	case reflect.Invalid:
		return true
	}
	return false
}
func (*NullSchema) MarshalJSON() ([]byte, error) {
	return []byte(`"null"`), nil
}
type RecordSchema struct {
	Name       string   `json:"name,omitempty"`
	Namespace  string   `json:"namespace,omitempty"`
	Doc        string   `json:"doc,omitempty"`
	Aliases    []string `json:"aliases,omitempty"`
	Properties map[string]interface{}
	Fields     []*SchemaField `json:"fields"`
}
func (s *RecordSchema) String() string {
	bytes, err := json.MarshalIndent(s, "", "    ")
	if err != nil {
		panic(err)
	}
	return string(bytes)
}
func (s *RecordSchema) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type      string         `json:"type,omitempty"`
		Namespace string         `json:"namespace,omitempty"`
		Name      string         `json:"name,omitempty"`
		Doc       string         `json:"doc,omitempty"`
		Aliases   []string       `json:"aliases,omitempty"`
		Fields    []*SchemaField `json:"fields"`
	}{
		Type:      "record",
		Namespace: s.Namespace,
		Name:      s.Name,
		Doc:       s.Doc,
		Aliases:   s.Aliases,
		Fields:    s.Fields,
	})
}
func (*RecordSchema) Type() int {
	return Record
}
func (s *RecordSchema) GetName() string {
	return s.Name
}
func (s *RecordSchema) Prop(key string) (interface{}, bool) {
	if s.Properties != nil {
		if prop, ok := s.Properties[key]; ok {
			return prop, true
		}
	}
	return nil, false
}
func (s *RecordSchema) Validate(v reflect.Value) bool {
	v = dereference(v)
	if v.Kind() != reflect.Struct || !v.CanAddr() || !v.CanInterface() {
		return false
	}
	rec, ok := v.Interface().(GenericRecord)
	if !ok {
		return v.Kind() == reflect.Struct
	}
	fieldCount := 0
	for key, val := range rec.fields {
		for idx := range s.Fields {
			if len(s.Fields[idx].Name) <= len(key) {
				lhs := key[len(key)-len(s.Fields[idx].Name):]
				if lhs == s.Fields[idx].Name {
					if !s.Fields[idx].Type.Validate(reflect.ValueOf(val)) {
						return false
					}
					fieldCount++
					break
				}
			}
		}
	}
	if fieldCount < len(rec.fields) {
		return false
	}
	return true
}
type RecursiveSchema struct {
	Actual *RecordSchema
}
func newRecursiveSchema(parent *RecordSchema) *RecursiveSchema {
	return &RecursiveSchema{
		Actual: parent,
	}
}
func (s *RecursiveSchema) String() string {
	return fmt.Sprintf(`{"type": "%s"}`, s.Actual.GetName())
}
func (*RecursiveSchema) Type() int {
	return Recursive
}
func (s *RecursiveSchema) GetName() string {
	return s.Actual.GetName()
}
func (*RecursiveSchema) Prop(key string) (interface{}, bool) {
	return nil, false
}
func (s *RecursiveSchema) Validate(v reflect.Value) bool {
	return s.Actual.Validate(v)
}
func (s *RecursiveSchema) MarshalJSON() ([]byte, error) {
	return []byte(fmt.Sprintf(`"%s"`, s.Actual.GetName())), nil
}
type SchemaField struct {
	Name       string      `json:"name,omitempty"`
	Doc        string      `json:"doc,omitempty"`
	Default    interface{} `json:"default"`
	Type       Schema      `json:"type,omitempty"`
	Properties map[string]interface{}
}
func (this *SchemaField) Prop(key string) (interface{}, bool) {
	if this.Properties != nil {
		if prop, ok := this.Properties[key]; ok {
			return prop, true
		}
	}
	return nil, false
}
func (s *SchemaField) MarshalJSON() ([]byte, error) {
	if s.Type.Type() == Null || (s.Type.Type() == Union && s.Type.(*UnionSchema).Types[0].Type() == Null) {
		return json.Marshal(struct {
			Name    string      `json:"name,omitempty"`
			Doc     string      `json:"doc,omitempty"`
			Default interface{} `json:"default"`
			Type    Schema      `json:"type,omitempty"`
		}{
			Name:    s.Name,
			Doc:     s.Doc,
			Default: s.Default,
			Type:    s.Type,
		})
	}
	return json.Marshal(struct {
		Name    string      `json:"name,omitempty"`
		Doc     string      `json:"doc,omitempty"`
		Default interface{} `json:"default,omitempty"`
		Type    Schema      `json:"type,omitempty"`
	}{
		Name:    s.Name,
		Doc:     s.Doc,
		Default: s.Default,
		Type:    s.Type,
	})
}
func (s *SchemaField) String() string {
	return fmt.Sprintf("[SchemaField: Name: %s, Doc: %s, Default: %v, Type: %s]", s.Name, s.Doc, s.Default, s.Type)
}
type EnumSchema struct {
	Name       string
	Namespace  string
	Aliases    []string
	Doc        string
	Symbols    []string
	Properties map[string]interface{}
}
func (s *EnumSchema) String() string {
	bytes, err := json.MarshalIndent(s, "", "    ")
	if err != nil {
		panic(err)
	}
	return string(bytes)
}
func (*EnumSchema) Type() int {
	return Enum
}
func (s *EnumSchema) GetName() string {
	return s.Name
}
func (s *EnumSchema) Prop(key string) (interface{}, bool) {
	if s.Properties != nil {
		if prop, ok := s.Properties[key]; ok {
			return prop, true
		}
	}
	return nil, false
}
func (*EnumSchema) Validate(v reflect.Value) bool {
	return true
}
func (s *EnumSchema) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type      string   `json:"type,omitempty"`
		Namespace string   `json:"namespace,omitempty"`
		Name      string   `json:"name,omitempty"`
		Doc       string   `json:"doc,omitempty"`
		Symbols   []string `json:"symbols,omitempty"`
	}{
		Type:      "enum",
		Namespace: s.Namespace,
		Name:      s.Name,
		Doc:       s.Doc,
		Symbols:   s.Symbols,
	})
}
type ArraySchema struct {
	Items      Schema
	Properties map[string]interface{}
}
func (s *ArraySchema) String() string {
	bytes, err := json.MarshalIndent(s, "", "    ")
	if err != nil {
		panic(err)
	}
	return string(bytes)
}
func (*ArraySchema) Type() int {
	return Array
}
func (*ArraySchema) GetName() string {
	return typeArray
}
func (s *ArraySchema) Prop(key string) (interface{}, bool) {
	if s.Properties != nil {
		if prop, ok := s.Properties[key]; ok {
			return prop, true
		}
	}
	return nil, false
}
func (s *ArraySchema) Validate(v reflect.Value) bool {
	v = dereference(v)
	return v.Kind() == reflect.Slice || v.Kind() == reflect.Array
}
func (s *ArraySchema) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type  string `json:"type,omitempty"`
		Items Schema `json:"items,omitempty"`
	}{
		Type:  "array",
		Items: s.Items,
	})
}
type MapSchema struct {
	Values     Schema
	Properties map[string]interface{}
}
func (s *MapSchema) String() string {
	bytes, err := json.MarshalIndent(s, "", "    ")
	if err != nil {
		panic(err)
	}
	return string(bytes)
}
func (*MapSchema) Type() int {
	return Map
}
func (*MapSchema) GetName() string {
	return typeMap
}
func (s *MapSchema) Prop(key string) (interface{}, bool) {
	if s.Properties != nil {
		if prop, ok := s.Properties[key]; ok {
			return prop, true
		}
	}
	return nil, false
}
func (s *MapSchema) Validate(v reflect.Value) bool {
	v = dereference(v)
	return v.Kind() == reflect.Map && v.Type().Key().Kind() == reflect.String
}
func (s *MapSchema) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type   string `json:"type,omitempty"`
		Values Schema `json:"values,omitempty"`
	}{
		Type:   "map",
		Values: s.Values,
	})
}
type UnionSchema struct {
	Types []Schema
}
func (s *UnionSchema) String() string {
	bytes, err := json.MarshalIndent(s, "", "    ")
	if err != nil {
		panic(err)
	}
	return fmt.Sprintf(`{"type": %s}`, string(bytes))
}
func (*UnionSchema) Type() int {
	return Union
}
func (*UnionSchema) GetName() string {
	return typeUnion
}
func (*UnionSchema) Prop(key string) (interface{}, bool) {
	return nil, false
}
func (s *UnionSchema) GetType(v reflect.Value) int {
	if s.Types != nil {
		for i := range s.Types {
			if t := s.Types[i]; t.Validate(v) {
				return i
			}
		}
	}
	return -1
}
func (s *UnionSchema) Validate(v reflect.Value) bool {
	v = dereference(v)
	for i := range s.Types {
		if t := s.Types[i]; t.Validate(v) {
			return true
		}
	}
	return false
}
func (s *UnionSchema) MarshalJSON() ([]byte, error) {
	return json.Marshal(s.Types)
}
type FixedSchema struct {
	Namespace  string
	Name       string
	Size       int
	Properties map[string]interface{}
}
func (s *FixedSchema) String() string {
	bytes, err := json.MarshalIndent(s, "", "    ")
	if err != nil {
		panic(err)
	}
	return string(bytes)
}
func (*FixedSchema) Type() int {
	return Fixed
}
func (s *FixedSchema) GetName() string {
	return s.Name
}
func (s *FixedSchema) Prop(key string) (interface{}, bool) {
	if s.Properties != nil {
		if prop, ok := s.Properties[key]; ok {
			return prop, true
		}
	}
	return nil, false
}
func (s *FixedSchema) Validate(v reflect.Value) bool {
	v = dereference(v)
	return (v.Kind() == reflect.Array || v.Kind() == reflect.Slice) && v.Type().Elem().Kind() == reflect.Uint8 && v.Len() == s.Size
}
func (s *FixedSchema) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type string `json:"type,omitempty"`
		Size int    `json:"size,omitempty"`
		Name string `json:"name,omitempty"`
	}{
		Type: "fixed",
		Size: s.Size,
		Name: s.Name,
	})
}
func GetFullName(schema Schema) string {
	switch sch := schema.(type) {
	case *RecordSchema:
		return getFullName(sch.GetName(), sch.Namespace)
	case *EnumSchema:
		return getFullName(sch.GetName(), sch.Namespace)
	case *FixedSchema:
		return getFullName(sch.GetName(), sch.Namespace)
	default:
		return schema.GetName()
	}
}
func ParseSchemaFile(file string) (Schema, error) {
	fileContents, err := ioutil.ReadFile(file)
	if err != nil {
		return nil, err
	}
	return ParseSchema(string(fileContents))
}