func FlatFieldValueByName(stru interface{}, nm string) reflect.Value {
	vv := reflect.ValueOf(stru)
	if stru == nil || vv.Kind() != reflect.Ptr {
		log.Printf("kit.FlatFieldsValueFunc: must pass a non-nil pointer to the struct: %v\n", stru)
		return reflect.Value{}
	}
	v := NonPtrValue(vv)
	return v.FieldByName(nm)
}
FlatFieldValueByName finds field in object and embedded objects, by name,
returning reflect.Value of field -- native version of Value function
already does flat find, so this just provides a convenient wrapper
package kit
import (
	"log"
	"reflect"
	"strings"
)
func FlatFieldsTypeFunc(typ reflect.Type, fun func(typ reflect.Type, field reflect.StructField) bool) bool {
	typ = NonPtrType(typ)
	if typ.Kind() != reflect.Struct {
		log.Printf("kit.FlatFieldsTypeFunc: Must call on a struct type, not: %v\n", typ)
		return false
	}
	rval := true
	for i := 0; i < typ.NumField(); i++ {
		f := typ.Field(i)
		if f.Type.Kind() == reflect.Struct && f.Anonymous {
			rval = FlatFieldsTypeFunc(f.Type, fun)  
			if !rval {
				break
			}
		} else {
			rval = fun(typ, f)
			if !rval {
				break
			}
		}
	}
	return rval
}
func AllFieldsTypeFunc(typ reflect.Type, fun func(typ reflect.Type, field reflect.StructField) bool) bool {
	typ = NonPtrType(typ)
	if typ.Kind() != reflect.Struct {
		log.Printf("kit.AllFieldsTypeFunc: Must call on a struct type, not: %v\n", typ)
		return false
	}
	rval := true
	for i := 0; i < typ.NumField(); i++ {
		f := typ.Field(i)
		if f.Type.Kind() == reflect.Struct {
			rval = AllFieldsTypeFunc(f.Type, fun)  
			if !rval {
				break
			}
		} else {
			rval = fun(typ, f)
			if !rval {
				break
			}
		}
	}
	return rval
}
func FlatFieldsValueFunc(stru interface{}, fun func(stru interface{}, typ reflect.Type, field reflect.StructField, fieldVal reflect.Value) bool) bool {
	vv := reflect.ValueOf(stru)
	if stru == nil || vv.Kind() != reflect.Ptr {
		log.Printf("kit.FlatFieldsValueFunc: must pass a non-nil pointer to the struct: %v\n", stru)
		return false
	}
	v := NonPtrValue(vv)
	if !v.IsValid() {
		return true
	}
	typ := v.Type()
	if typ.Kind() != reflect.Struct {
		return false
	}
	rval := true
	for i := 0; i < typ.NumField(); i++ {
		f := typ.Field(i)
		vf := v.Field(i)
		if !vf.CanInterface() {
			continue
		}
		vfi := vf.Interface()
		if vfi == stru {
			continue
		}
		if f.Type.Kind() == reflect.Struct && f.Anonymous {
			rval = FlatFieldsValueFunc(PtrValue(vf).Interface(), fun)
			if !rval {
				break
			}
		} else {
			rval = fun(vfi, typ, f, vf)
			if !rval {
				break
			}
		}
	}
	return rval
}
func FlatFields(typ reflect.Type) []reflect.StructField {
	ff := make([]reflect.StructField, 0)
	falseErr := FlatFieldsTypeFunc(typ, func(typ reflect.Type, field reflect.StructField) bool {
		ff = append(ff, field)
		return true
	})
	if falseErr == false {
		return nil
	}
	return ff
}
func AllFields(typ reflect.Type) []reflect.StructField {
	ff := make([]reflect.StructField, 0)
	falseErr := AllFieldsTypeFunc(typ, func(typ reflect.Type, field reflect.StructField) bool {
		ff = append(ff, field)
		return true
	})
	if falseErr == false {
		return nil
	}
	return ff
}
func AllFieldsN(typ reflect.Type) int {
	n := 0
	falseErr := AllFieldsTypeFunc(typ, func(typ reflect.Type, field reflect.StructField) bool {
		n++
		return true
	})
	if falseErr == false {
		return 0
	}
	return n
}
func FlatFieldVals(stru interface{}) []reflect.Value {
	ff := make([]reflect.Value, 0)
	falseErr := FlatFieldsValueFunc(stru, func(stru interface{}, typ reflect.Type, field reflect.StructField, fieldVal reflect.Value) bool {
		ff = append(ff, fieldVal)
		return true
	})
	if falseErr == false {
		return nil
	}
	return ff
}
func FlatFieldInterfaces(stru interface{}) []interface{} {
	ff := make([]interface{}, 0)
	falseErr := FlatFieldsValueFunc(stru, func(stru interface{}, typ reflect.Type, field reflect.StructField, fieldVal reflect.Value) bool {
		ff = append(ff, PtrValue(fieldVal).Interface())
		return true
	})
	if falseErr == false {
		return nil
	}
	return ff
}
func FlatFieldByName(typ reflect.Type, nm string) (reflect.StructField, bool) {
	return typ.FieldByName(nm)
}
func FieldByPath(typ reflect.Type, path string) (reflect.StructField, bool) {
	pels := strings.Split(path, ".")
	ctyp := typ
	plen := len(pels)
	for i, pe := range pels {
		fld, ok := ctyp.FieldByName(pe)
		if !ok {
			log.Printf("kit.FieldByPath: field: %v not found in type: %v, starting from path: %v, in type: %v\n", pe, ctyp.String(), path, typ.String())
			return fld, false
		}
		if i == plen-1 {
			return fld, true
		}
		ctyp = fld.Type
	}
	return reflect.StructField{}, false
}
func FieldValueByPath(stru interface{}, path string) (reflect.Value, bool) {
	pels := strings.Split(path, ".")
	sval := reflect.ValueOf(stru)
	cval := sval
	typ := sval.Type()
	ctyp := typ
	plen := len(pels)
	for i, pe := range pels {
		_, ok := ctyp.FieldByName(pe)
		if !ok {
			log.Printf("kit.FieldValueByPath: field: %v not found in type: %v, starting from path: %v, in type: %v\n", pe, cval.Type().String(), path, typ.String())
			return cval, false
		}
		fval := cval.FieldByName(pe)
		if i == plen-1 {
			return fval, true
		}
		cval = fval
		ctyp = fval.Type()
	}
	return reflect.Value{}, false
}
func FlatFieldTag(typ reflect.Type, nm, tag string) string {
	fld, ok := typ.FieldByName(nm)
	if !ok {
		return ""
	}
	return fld.Tag.Get(tag)
}