func plsFieldSave(ctx context.Context, props *[]Property, p Property, name string, opts saveOpts, v reflect.Value) (ok bool, err error) {
	vpls, err := plsForSave(v)
	if err != nil {
		return false, err
	}
	if vpls == nil {
		return false, nil
	}
	subProps, err := vpls.Save(ctx)
	if err != nil {
		return true, err
	}
	if opts.flatten {
		for _, subp := range subProps {
			subp.Name = name + "." + subp.Name
			*props = append(*props, subp)
		}
		return true, nil
	}
	p.Value = &Entity{Properties: subProps}
	*props = append(*props, p)
	return true, nil
}
plsFieldSave first tries to converts v's value to a PLS, then v's addressed
value to a PLS. If neither succeeds, plsFieldSave returns false for first return
value.
If v is successfully converted to a PLS, plsFieldSave will then add the
Value to property p by way of the PLS's Save method, and append it to props.

If the flatten option is present in opts, name must be prepended to each property's
name before it is appended to props. Eg. if name were "A" and a subproperty's name
were "B", the resultant name of the property to be appended to props would be "A.B".
package datastore
import (
	"context"
	"errors"
	"fmt"
	"reflect"
	"time"
)
type saveOpts struct {
	noIndex   bool
	flatten   bool
	omitEmpty bool
}
func saveEntity(ctx context.Context, key Key, src interface{}) (*Entity, error) {
	var err error
	var props []Property
	if e, ok := src.(PropertyLoadSaver); ok {
		props, err = e.Save(ctx)
	} else {
		props, err = SaveStruct(ctx, src)
	}
	if err != nil {
		return nil, err
	}
	entity, err := propertiesToProtoFake(ctx, key, props)
	if err != nil {
		return nil, err
	}
	return entity, nil
}
func saveStructProperty(ctx context.Context, props *[]Property, name string, opts saveOpts, v reflect.Value) error {
	p := Property{
		Name:    name,
		NoIndex: opts.noIndex,
	}
	if opts.omitEmpty && isEmptyValue(v) {
		return nil
	}
	ok, err := plsFieldSave(ctx, props, p, name, opts, v)
	if err != nil {
		return err
	}
	if ok {
		return nil
	}
	if v.Type().AssignableTo(typeOfKey) {
		p.Value = v.Interface()
	} else {
		switch x := v.Interface().(type) {
		case time.Time, GeoPoint:
			p.Value = x
		case PropertyTranslator:
			v, err := x.ToPropertyValue(ctx)
			if err != nil {
				return err
			}
			p.Value = v
		default:
			switch v.Kind() {
			case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
				p.Value = v.Int()
			case reflect.Bool:
				p.Value = v.Bool()
			case reflect.String:
				p.Value = v.String()
			case reflect.Float32, reflect.Float64:
				p.Value = v.Float()
			case reflect.Slice:
				if v.Type().Elem().Kind() == reflect.Uint8 {
					p.Value = v.Bytes()
				} else {
					return saveSliceProperty(ctx, props, name, opts, v)
				}
			case reflect.Ptr:
				if v.Type().Elem().Kind() != reflect.Struct {
					return fmt.Errorf("datastore: unsupported struct field type: %s", v.Type())
				}
				if v.IsNil() {
					return nil
				}
				v = v.Elem()
				fallthrough
			case reflect.Struct:
				if !v.CanAddr() {
					return fmt.Errorf("datastore: unsupported struct field: value is unaddressable")
				}
				vi := v.Addr().Interface()
				sub, err := newStructPLS(vi)
				if err != nil {
					return fmt.Errorf("datastore: unsupported struct field: %v", err)
				}
				if opts.flatten {
					return sub.save(ctx, props, opts, name+".")
				}
				var subProps []Property
				err = sub.save(ctx, &subProps, opts, "")
				if err != nil {
					return err
				}
				subKey, err := sub.key(v)
				if err != nil {
					return err
				}
				p.Value = &Entity{
					Key:        subKey,
					Properties: subProps,
				}
			}
		}
		if p.Value == nil {
			return fmt.Errorf("datastore: unsupported struct field type: %v", v.Type())
		}
	}
	*props = append(*props, p)
	return nil
}