go.mercari.io/datastore@v1.8.2/internal/shared/ops.go (about)

     1  package shared
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"reflect"
     8  
     9  	"go.mercari.io/datastore"
    10  )
    11  
    12  var typeOfPropertyLoadSaver = reflect.TypeOf((*datastore.PropertyLoadSaver)(nil)).Elem()
    13  var typeOfPropertyList = reflect.TypeOf(datastore.PropertyList(nil))
    14  
    15  type getOps func(keys []datastore.Key, dst []datastore.PropertyList) error
    16  type putOps func(keys []datastore.Key, src []datastore.PropertyList) ([]datastore.Key, []datastore.PendingKey, error)
    17  type deleteOps func(keys []datastore.Key) error
    18  type nextOps func(dst *datastore.PropertyList) (datastore.Key, error)
    19  type getAllOps func(dst *[]datastore.PropertyList) ([]datastore.Key, error)
    20  
    21  func GetMultiOps(ctx context.Context, keys []datastore.Key, dst interface{}, ops getOps) error {
    22  	v := reflect.ValueOf(dst)
    23  	if v.Kind() != reflect.Slice {
    24  		return errors.New("datastore: dst has invalid type")
    25  	}
    26  	if len(keys) != v.Len() {
    27  		return errors.New("datastore: keys and dst slices have different length")
    28  	}
    29  	if len(keys) == 0 {
    30  		return nil
    31  	}
    32  
    33  	pss := make([]datastore.PropertyList, len(keys))
    34  	err := ops(keys, pss)
    35  	foundError := false
    36  
    37  	merr, catchMerr := err.(datastore.MultiError)
    38  	if catchMerr {
    39  		// ok
    40  		if len(merr) != len(keys) {
    41  			panic(fmt.Sprintf("unexpected merr length: %d, expected: %d", len(merr), len(keys)))
    42  		}
    43  	} else if err == nil {
    44  		merr = make([]error, len(keys))
    45  	} else if err != nil {
    46  		return err
    47  	}
    48  
    49  	elemType := v.Type().Elem()
    50  	for idx := range keys {
    51  		if catchMerr {
    52  			err := merr[idx]
    53  			if err != nil {
    54  				foundError = true
    55  				continue
    56  			}
    57  		}
    58  
    59  		elem := v.Index(idx)
    60  		if reflect.PtrTo(elemType).Implements(typeOfPropertyLoadSaver) {
    61  			elem = elem.Addr()
    62  		} else if elemType.Kind() == reflect.Struct {
    63  			elem = elem.Addr()
    64  		} else if elemType.Kind() == reflect.Ptr && elemType.Elem().Kind() == reflect.Struct {
    65  			if elem.IsNil() {
    66  				elem.Set(reflect.New(elem.Type().Elem()))
    67  			}
    68  		}
    69  
    70  		if err = datastore.LoadEntity(ctx, elem.Interface(), &datastore.Entity{Key: keys[idx], Properties: pss[idx]}); err != nil {
    71  			merr[idx] = err
    72  			foundError = true
    73  		}
    74  	}
    75  
    76  	if foundError {
    77  		return datastore.MultiError(merr)
    78  	}
    79  
    80  	return nil
    81  }
    82  
    83  func PutMultiOps(ctx context.Context, keys []datastore.Key, src interface{}, ops putOps) ([]datastore.Key, []datastore.PendingKey, error) {
    84  	v := reflect.ValueOf(src)
    85  	if v.Kind() != reflect.Slice {
    86  		return nil, nil, errors.New("datastore: src has invalid type")
    87  	}
    88  	if len(keys) != v.Len() {
    89  		return nil, nil, errors.New("datastore: key and src slices have different length")
    90  	}
    91  	if len(keys) == 0 {
    92  		return nil, nil, nil
    93  	}
    94  
    95  	var pss []datastore.PropertyList
    96  	for idx, key := range keys {
    97  		elem := v.Index(idx)
    98  		if reflect.PtrTo(elem.Type()).Implements(typeOfPropertyLoadSaver) || elem.Type().Kind() == reflect.Struct {
    99  			elem = elem.Addr()
   100  		}
   101  		src := elem.Interface()
   102  		e, err := datastore.SaveEntity(ctx, key, src)
   103  		if err != nil {
   104  			return nil, nil, err
   105  		}
   106  		pss = append(pss, e.Properties)
   107  	}
   108  
   109  	keys, pKeys, err := ops(keys, pss)
   110  	if err != nil {
   111  		return nil, nil, err
   112  	}
   113  
   114  	return keys, pKeys, nil
   115  }
   116  
   117  func DeleteMultiOps(ctx context.Context, keys []datastore.Key, ops deleteOps) error {
   118  	return ops(keys)
   119  }
   120  
   121  func NextOps(ctx context.Context, qDump *datastore.QueryDump, dst interface{}, ops nextOps) (datastore.Key, error) {
   122  
   123  	// don't pass nil to ops.
   124  	// the true query may not be KeysOnly.
   125  	var ps datastore.PropertyList
   126  	key, err := ops(&ps)
   127  	if err != nil {
   128  		return nil, err
   129  	}
   130  
   131  	if !qDump.KeysOnly {
   132  		if err = datastore.LoadEntity(ctx, dst, &datastore.Entity{Key: key, Properties: ps}); err != nil {
   133  			return key, err
   134  		}
   135  	}
   136  
   137  	return key, nil
   138  }
   139  
   140  func GetAllOps(ctx context.Context, qDump *datastore.QueryDump, dst interface{}, ops getAllOps) ([]datastore.Key, error) {
   141  	var dv reflect.Value
   142  	var elemType reflect.Type
   143  	var isPtrStruct bool
   144  	if !qDump.KeysOnly {
   145  		dv = reflect.ValueOf(dst)
   146  		if dv.Kind() != reflect.Ptr || dv.IsNil() {
   147  			return nil, datastore.ErrInvalidEntityType
   148  		}
   149  		dv = dv.Elem()
   150  		if dv.Kind() != reflect.Slice {
   151  			return nil, datastore.ErrInvalidEntityType
   152  		}
   153  		if dv.Type() == typeOfPropertyList {
   154  			return nil, datastore.ErrInvalidEntityType
   155  		}
   156  		elemType = dv.Type().Elem()
   157  		if reflect.PtrTo(elemType).Implements(typeOfPropertyLoadSaver) {
   158  			// ok
   159  		} else {
   160  			switch elemType.Kind() {
   161  			case reflect.Ptr:
   162  				isPtrStruct = true
   163  				elemType = elemType.Elem()
   164  				if elemType.Kind() != reflect.Struct {
   165  					return nil, datastore.ErrInvalidEntityType
   166  				}
   167  			}
   168  		}
   169  	}
   170  
   171  	// TODO add reflect.Map support
   172  
   173  	var pss []datastore.PropertyList
   174  	keys, err := ops(&pss)
   175  	if err != nil {
   176  		return nil, err
   177  	}
   178  
   179  	if !qDump.KeysOnly {
   180  		for idx, ps := range pss {
   181  
   182  			elem := reflect.New(elemType)
   183  
   184  			if err = datastore.LoadEntity(ctx, elem.Interface(), &datastore.Entity{Key: keys[idx], Properties: ps}); err != nil {
   185  				return nil, err
   186  			}
   187  
   188  			if !isPtrStruct {
   189  				elem = elem.Elem()
   190  			}
   191  
   192  			dv.Set(reflect.Append(dv, elem))
   193  		}
   194  	}
   195  
   196  	return keys, nil
   197  }