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 }