func (m *structMap) load(t reflect.Type, loaded *[]string) (spec *structSpec,
	err error) {
	defer func() {
		if r := recover(); r != nil {
			err = r.(error)
		}
	}()
	if t.Kind() != reflect.Struct {
		return nil, errors.New("Not a struct.")
	}
	structId := getTypeId(t)
	spec = &structSpec{fields: make(map[string]*structFieldSpec)}
	m.specs[structId] = spec
	*loaded = append(*loaded, structId)
	var toLoad reflect.Type
	uniqueNames := make([]string, t.NumField())
	for i := 0; i < t.NumField(); i++ {
		field := t.Field(i)
		if !isSupportedType(field.Type) {
			continue
		}
		toLoad = nil
		switch field.Type.Kind() {
		case reflect.Map, reflect.Slice:
			et := field.Type.Elem()
			if et.Kind() == reflect.Struct {
				toLoad = et
			}
		case reflect.Struct:
			toLoad = field.Type
		}
		if toLoad != nil {
			structId = getTypeId(toLoad)
			if m.specs[structId] == nil {
				if _, err = m.load(toLoad, loaded); err != nil {
					return nil, err
				}
			}
		}
		name := field.Tag.Get("schema-name")
		if name == "" {
			name = field.Name
		}
		for _, uniqueName := range uniqueNames {
			if name == uniqueName {
				return nil, errors.New("Field names and name tags in a " +
					"struct must be unique.")
			}
		}
		uniqueNames[i] = name
		tags := make([]string, 0)
		tagStrings := field.Tag.Get("schema-tags")
		for _, tag := range strings.Split(tagStrings, " ") {
			tag := strings.TrimSpace(tag)
			if tag != "" {
				tags = append(tags, tag)
			}
		}
		spec.fields[name] = &structFieldSpec{
			name:     name,
			realName: field.Name,
			tags:     tags,
		}
	}
	return
}
load caches parsed struct metadata.

It is an internal function used by getOrLoad and can't be called directly
because a write lock is required.

The loaded argument is the list of keys to roll back in case of error.
package schema
import (
	"errors"
	"fmt"
	"reflect"
	"strconv"
	"strings"
	"sync"
)
type TypeConv func(basic reflect.Value) reflect.Value
var conversionMap = make(map[string]TypeConv)
func AddTypeConverter(rt reflect.Type, conv TypeConv) {
	conversionMap[getTypeId(rt)] = conv
}
func getTypeConverter(rt reflect.Type) TypeConv {
	if conv, ok := conversionMap[getTypeId(rt)]; ok {
		return conv
	}
	return nil
}
type ValueError struct {
	err []error
}
func (e *ValueError) Errors() []error {
	return e.err
}
func (e *ValueError) Add(err error) {
	if e.err == nil {
		e.err = make([]error, 0)
	}
	e.err = append(e.err, err)
}
func (e *ValueError) Error() string {
	if e.err == nil {
		return ""
	}
	return fmt.Sprintf("%v", e.err)
}
type SchemaError struct {
	err map[string][]error
}
func (e *SchemaError) Errors() map[string][]error {
	return e.err
}
func (e *SchemaError) Err(key string) []error {
	if e.err != nil {
		if v, ok := e.err[key]; ok {
			return v
		}
	}
	return nil
}
func (e *SchemaError) Add(err error, key string, index int) {
	if e.err == nil {
		e.err = make(map[string][]error)
	}
	v1, ok := e.err[key]
	if !ok || index+1 > cap(v1) {
		newV := make([]error, index+1)
		if ok {
			copy(newV, v1)
		}
		v1 = newV
	}
	v2 := v1[index]
	if v2 == nil {
		v2 = &ValueError{}
		v1[index] = v2
	}
	(v2.(*ValueError)).Add(err)
	e.err[key] = v1
}
func (e *SchemaError) Error() string {
	if e.err == nil {
		return ""
	}
	return fmt.Sprintf("%v", e.err)
}
func (e *SchemaError) New() string {
	if e.err == nil {
		return ""
	}
	return fmt.Sprintf("%v", e.err)
}
func Load(i interface{}, data map[string][]string) error {
	return loadAndValidate(i, data, nil, nil)
}
func loadAndValidate(i interface{}, data map[string][]string,
	filters map[string]string, validators map[string]string) error {
	err := &SchemaError{}
	val := reflect.ValueOf(i)
	if val.Kind() != reflect.Ptr || val.Elem().Kind() != reflect.Struct {
		err.Add(errors.New("Interface must be a pointer to struct."), "", 0)
	} else {
		rv := val.Elem()
		for path, values := range data {
			parts := strings.Split(path, ".")
			loadValue(rv, values, parts, path, err)
		}
	}
	if err.Error() == "" {
		return nil
	}
	return err
}
func loadValue(rv reflect.Value, values, parts []string, key string,
	se *SchemaError) {
	spec, err := defaultStructMap.getOrLoad(rv.Type())
	if err != nil {
		se.Add(err, "", 0)
		return
	}
	fieldSpec, ok := spec.fields[parts[0]]
	if !ok {
		return
	}
	parts = parts[1:]
	field := setIndirect(rv.FieldByName(fieldSpec.realName))
	kind := field.Kind()
	if (kind == reflect.Struct || (kind == reflect.Slice && len(parts) > 0) || kind == reflect.Map) == (len(parts) == 0) {
		return
	}
	var idx string
	if kind == reflect.Map {
		idx = parts[0]
		parts = parts[1:]
		if len(parts) > 0 {
			return
		}
	}
	if len(parts) > 0 {
		if kind == reflect.Slice {
			if field.IsNil() {
				slice := reflect.MakeSlice(field.Type(), len(values), len(values))
				field.Set(slice)
			}
			for i := 0; i < len(values); i++ {
				sv := field.Index(i)
				loadValue(sv, values[i:i+1], parts, key, se)
			}
		} else {
			loadValue(field, values, parts, key, se)
		}
		return
	}
	var value reflect.Value
	switch kind {
	case reflect.Bool,
		reflect.Float32, reflect.Float64,
		reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
		reflect.String,
		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
		reflect.Uint64:
		value, err = coerce(kind, values[0])
		if err != nil {
			se.Add(err, key, 0)
		} else {
			if conv := getTypeConverter(field.Type()); conv != nil {
				value = conv(value)
			}
			field.Set(value)
		}
	case reflect.Map:
		elem := field.Type().Elem()
		ekind := elem.Kind()
		if field.IsNil() {
			field.Set(reflect.MakeMap(field.Type()))
		}
		value, err = coerce(ekind, values[0])
		if err != nil {
			value = reflect.New(elem)
			se.Add(err, key, 0)
		}
		if conv := getTypeConverter(elem); conv != nil {
			value = conv(value)
		}
		field.SetMapIndex(reflect.ValueOf(idx), value)
	case reflect.Slice:
		elem := field.Type().Elem()
		ekind := elem.Kind()
		slice := reflect.MakeSlice(field.Type(), 0, 0)
		conv := getTypeConverter(elem)
		for k, v := range values {
			value, err = coerce(ekind, v)
			if err != nil {
				value = reflect.New(elem)
				se.Add(err, key, k)
			}
			if conv != nil {
				value = conv(value)
			}
			slice = reflect.Append(slice, value)
		}
		field.Set(slice)
	}
	return
}
func coerce(kind reflect.Kind, value string) (rv reflect.Value, err error) {
	switch kind {
	case reflect.Bool:
		var v bool
		v, err = strconv.Atob(value)
		rv = reflect.ValueOf(v)
	case reflect.Float32:
		var v float32
		v, err = strconv.Atof32(value)
		rv = reflect.ValueOf(v)
	case reflect.Float64:
		var v float64
		v, err = strconv.Atof64(value)
		rv = reflect.ValueOf(v)
	case reflect.Int:
		var v int
		v, err = strconv.Atoi(value)
		rv = reflect.ValueOf(v)
	case reflect.Int8:
		var v int
		v, err = strconv.Atoi(value)
		rv = reflect.ValueOf(int8(v))
	case reflect.Int16:
		var v int
		v, err = strconv.Atoi(value)
		rv = reflect.ValueOf(int16(v))
	case reflect.Int32:
		var v int
		v, err = strconv.Atoi(value)
		rv = reflect.ValueOf(int32(v))
	case reflect.Int64:
		var v int64
		v, err = strconv.Atoi64(value)
		rv = reflect.ValueOf(v)
	case reflect.String:
		rv = reflect.ValueOf(value)
	case reflect.Uint:
		var v uint
		v, err = strconv.Atoui(value)
		rv = reflect.ValueOf(v)
	case reflect.Uint8:
		var v uint
		v, err = strconv.Atoui(value)
		rv = reflect.ValueOf(uint8(v))
	case reflect.Uint16:
		var v uint
		v, err = strconv.Atoui(value)
		rv = reflect.ValueOf(uint16(v))
	case reflect.Uint32:
		var v uint
		v, err = strconv.Atoui(value)
		rv = reflect.ValueOf(uint32(v))
	case reflect.Uint64:
		var v uint64
		v, err = strconv.Atoui64(value)
		rv = reflect.ValueOf(v)
	default:
		err = errors.New("Unsupported type.")
	}
	return
}
var defaultStructMap = newStructMap()
type structMap struct {
	specs map[string]*structSpec
	mutex sync.RWMutex
}
func newStructMap() *structMap {
	return &structMap{
		specs: make(map[string]*structSpec),
	}
}
func (m *structMap) getByType(t reflect.Type) (spec *structSpec) {
	if m.specs != nil && t.Kind() == reflect.Struct {
		m.mutex.RLock()
		spec = m.specs[getTypeId(t)]
		m.mutex.RUnlock()
	}
	return
}
func (m *structMap) getOrLoad(t reflect.Type) (spec *structSpec,
	err error) {
	if spec = m.getByType(t); spec != nil {
		return spec, nil
	}
	m.mutex.Lock()
	loaded := make([]string, 0)
	if spec, err = m.load(t, &loaded); err != nil {
		for _, v := range loaded {
			delete(m.specs, v)
		}
		return
	}
	m.mutex.Unlock()
	return
}