go.mercari.io/datastore@v1.8.2/prop.go (about) 1 // Copyright 2014 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package datastore 16 17 import ( 18 "context" 19 "fmt" 20 "reflect" 21 "strings" 22 "unicode" 23 24 "go.mercari.io/datastore/internal/c/fields" 25 ) 26 27 // SuppressErrFieldMismatch when this flag is true. 28 // If you want to align (AE|Cloud) Datastore's default behavior, set false. 29 var SuppressErrFieldMismatch = true 30 31 var _ PropertyLoadSaver = (*PropertyList)(nil) 32 33 // Entities with more than this many indexed properties will not be saved. 34 // const maxIndexedProperties = 20000 35 36 // Property is a name/value pair plus some metadata. A datastore entity's 37 // contents are loaded and saved as a sequence of Properties. Each property 38 // name must be unique within an entity. 39 type Property struct { 40 // Name is the property name. 41 Name string 42 // Value is the property value. The valid types are: 43 // - int64 44 // - bool 45 // - string 46 // - float64 47 // - Key 48 // - time.Time (retrieved as local time) 49 // - GeoPoint 50 // - []byte (up to 1 megabyte in length) 51 // - *Entity (representing a nested struct) 52 // Value can also be: 53 // - []interface{} where each element is one of the above types 54 // This set is smaller than the set of valid struct field types that the 55 // datastore can load and save. A Value's type must be explicitly on 56 // the list above; it is not sufficient for the underlying type to be 57 // on that list. For example, a Value of "type myInt64 int64" is 58 // invalid. Smaller-width integers and floats are also invalid. Again, 59 // this is more restrictive than the set of valid struct field types. 60 // 61 // A Value will have an opaque type when loading entities from an index, 62 // such as via a projection query. Load entities into a struct instead 63 // of a PropertyLoadSaver when using a projection query. 64 // 65 // A Value may also be the nil interface value; this is equivalent to 66 // Python's None but not directly representable by a Go struct. Loading 67 // a nil-valued property into a struct will set that field to the zero 68 // value. 69 Value interface{} 70 // NoIndex is whether the datastore cannot index this property. 71 // If NoIndex is set to false, []byte and string values are limited to 72 // 1500 bytes. 73 NoIndex bool 74 } 75 76 // An Entity is the value type for a nested struct. 77 // This type is only used for a Property's Value. 78 type Entity struct { 79 Key Key 80 Properties []Property 81 } 82 83 // PropertyLoadSaver can be converted from and to a slice of Properties. 84 type PropertyLoadSaver interface { 85 Load(ctx context.Context, ps []Property) error 86 Save(ctx context.Context) ([]Property, error) 87 } 88 89 // KeyLoader can store a Key. 90 type KeyLoader interface { 91 // PropertyLoadSaver is embedded because a KeyLoader 92 // must also always implement PropertyLoadSaver. 93 PropertyLoadSaver 94 LoadKey(ctx context.Context, k Key) error 95 } 96 97 // PropertyList converts a []Property to implement PropertyLoadSaver. 98 type PropertyList []Property 99 100 // Load loads all of the provided properties into l. 101 // It does not first reset *l to an empty slice. 102 func (l *PropertyList) Load(ctx context.Context, p []Property) error { 103 *l = append(*l, p...) 104 return nil 105 } 106 107 // Save saves all of l's properties as a slice of Properties. 108 func (l *PropertyList) Save(ctx context.Context) ([]Property, error) { 109 return *l, nil 110 } 111 112 // validPropertyName returns whether name consists of one or more valid Go 113 // identifiers joined by ".". 114 func validPropertyName(name string) bool { 115 if name == "" { 116 return false 117 } 118 for _, s := range strings.Split(name, ".") { 119 if s == "" { 120 return false 121 } 122 first := true 123 for _, c := range s { 124 if first { 125 first = false 126 if c != '_' && !unicode.IsLetter(c) { 127 return false 128 } 129 } else { 130 if c != '_' && !unicode.IsLetter(c) && !unicode.IsDigit(c) { 131 return false 132 } 133 } 134 } 135 } 136 return true 137 } 138 139 // parseTag interprets datastore struct field tags 140 func parseTag(t reflect.StructTag) (name string, keep bool, other interface{}, err error) { 141 s := t.Get("datastore") 142 parts := strings.Split(s, ",") 143 if parts[0] == "-" && len(parts) == 1 { 144 return "", false, nil, nil 145 } 146 if parts[0] != "" && !validPropertyName(parts[0]) { 147 err = fmt.Errorf("datastore: struct tag has invalid property name: %q", parts[0]) 148 return "", false, nil, err 149 } 150 151 var opts saveOpts 152 if len(parts) > 1 { 153 for _, p := range parts[1:] { 154 switch p { 155 case "flatten": 156 opts.flatten = true 157 case "omitempty": 158 opts.omitEmpty = true 159 case "noindex": 160 opts.noIndex = true 161 default: 162 err = fmt.Errorf("datastore: struct tag has invalid option: %q", p) 163 return "", false, nil, err 164 } 165 } 166 other = opts 167 } 168 return parts[0], true, other, nil 169 } 170 171 func validateType(t reflect.Type) error { 172 if t.Kind() != reflect.Struct { 173 return fmt.Errorf("datastore: validate called with non-struct type %s", t) 174 } 175 176 return validateChildType(t, "", false, false, map[reflect.Type]bool{}) 177 } 178 179 // validateChildType is a recursion helper func for validateType 180 func validateChildType(t reflect.Type, fieldName string, flatten, prevSlice bool, prevTypes map[reflect.Type]bool) error { 181 if prevTypes[t] { 182 return nil 183 } 184 prevTypes[t] = true 185 186 switch t.Kind() { 187 case reflect.Slice: 188 if flatten && prevSlice { 189 return fmt.Errorf("datastore: flattening nested structs leads to a slice of slices: field %q", fieldName) 190 } 191 return validateChildType(t.Elem(), fieldName, flatten, true, prevTypes) 192 case reflect.Struct: 193 if t == typeOfTime || t == typeOfGeoPoint { 194 return nil 195 } 196 197 for i := 0; i < t.NumField(); i++ { 198 f := t.Field(i) 199 200 // If a named field is unexported, ignore it. An anonymous 201 // unexported field is processed, because it may contain 202 // exported fields, which are visible. 203 exported := (f.PkgPath == "") 204 if !exported && !f.Anonymous { 205 continue 206 } 207 208 _, keep, other, err := parseTag(f.Tag) 209 // Handle error from parseTag now instead of later (in cache.Fields call). 210 if err != nil { 211 return err 212 } 213 if !keep { 214 continue 215 } 216 if other != nil { 217 opts := other.(saveOpts) 218 flatten = flatten || opts.flatten 219 } 220 if err := validateChildType(f.Type, f.Name, flatten, prevSlice, prevTypes); err != nil { 221 return err 222 } 223 } 224 case reflect.Ptr: 225 if t == typeOfKey { 226 return nil 227 } 228 return validateChildType(t.Elem(), fieldName, flatten, prevSlice, prevTypes) 229 } 230 return nil 231 } 232 233 // isLeafType determines whether or not a type is a 'leaf type' 234 // and should not be recursed into, but considered one field. 235 func isLeafType(t reflect.Type) bool { 236 return t == typeOfTime || t == typeOfGeoPoint 237 } 238 239 // structCache collects the structs whose fields have already been calculated. 240 var structCache = fields.NewCache(parseTag, validateType, isLeafType) 241 242 // structPLS adapts a struct to be a PropertyLoadSaver. 243 type structPLS struct { 244 v reflect.Value 245 codec fields.List 246 } 247 248 // newStructPLS returns a structPLS, which implements the 249 // PropertyLoadSaver interface, for the struct pointer p. 250 func newStructPLS(p interface{}) (*structPLS, error) { 251 v := reflect.ValueOf(p) 252 if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct { 253 return nil, ErrInvalidEntityType 254 } 255 v = v.Elem() 256 f, err := structCache.Fields(v.Type()) 257 if err != nil { 258 return nil, err 259 } 260 return &structPLS{v, f}, nil 261 } 262 263 // LoadStruct loads the properties from p to dst. 264 // dst must be a struct pointer. 265 // 266 // The values of dst's unmatched struct fields are not modified, 267 // and matching slice-typed fields are not reset before appending to 268 // them. In particular, it is recommended to pass a pointer to a zero 269 // valued struct on each LoadStruct call. 270 func LoadStruct(ctx context.Context, dst interface{}, p []Property) error { 271 x, err := newStructPLS(dst) 272 if err != nil { 273 return err 274 } 275 return x.Load(ctx, p) 276 } 277 278 // SaveStruct returns the properties from src as a slice of Properties. 279 // src must be a struct pointer. 280 func SaveStruct(ctx context.Context, src interface{}) ([]Property, error) { 281 x, err := newStructPLS(src) 282 if err != nil { 283 return nil, err 284 } 285 return x.Save(ctx) 286 } 287 288 // plsForLoad tries to convert v to a PropertyLoadSaver. 289 // If successful, plsForLoad returns a settable v as a PropertyLoadSaver. 290 // 291 // plsForLoad is intended to be used with nested struct fields which 292 // may implement PropertyLoadSaver. 293 // 294 // v must be settable. 295 func plsForLoad(v reflect.Value) (PropertyLoadSaver, error) { 296 var nilPtr bool 297 if v.Kind() == reflect.Ptr && v.IsNil() { 298 nilPtr = true 299 v.Set(reflect.New(v.Type().Elem())) 300 } 301 302 vpls, err := pls(v) 303 if nilPtr && (vpls == nil || err != nil) { 304 // unset v 305 v.Set(reflect.Zero(v.Type())) 306 } 307 308 return vpls, err 309 } 310 311 // plsForSave tries to convert v to a PropertyLoadSaver. 312 // If successful, plsForSave returns v as a PropertyLoadSaver. 313 // 314 // plsForSave is intended to be used with nested struct fields which 315 // may implement PropertyLoadSaver. 316 // 317 // v must be settable. 318 func plsForSave(v reflect.Value) (PropertyLoadSaver, error) { 319 switch v.Kind() { 320 case reflect.Ptr, reflect.Slice, reflect.Map, reflect.Interface, reflect.Chan, reflect.Func: 321 // If v is nil, return early. v contains no data to save. 322 if v.IsNil() { 323 return nil, nil 324 } 325 } 326 327 return pls(v) 328 } 329 330 func pls(v reflect.Value) (PropertyLoadSaver, error) { 331 if v.Kind() != reflect.Ptr { 332 if _, ok := v.Interface().(PropertyLoadSaver); ok { 333 return nil, fmt.Errorf("datastore: PropertyLoadSaver methods must be implemented on a pointer to %T", v.Interface()) 334 } 335 336 v = v.Addr() 337 } 338 339 vpls, _ := v.Interface().(PropertyLoadSaver) 340 return vpls, nil 341 } 342 343 // ptForLoad returns PropertyTranslator and set zero value if needed. 344 // this function is peculiar to mercari/datastore. 345 func ptForLoad(v reflect.Value) (PropertyTranslator, error) { 346 var nilPtr bool 347 if v.Kind() == reflect.Ptr && v.IsNil() { 348 nilPtr = true 349 v.Set(reflect.New(v.Type().Elem())) 350 } 351 352 vpt, err := pt(v) 353 if nilPtr && (vpt == nil || err != nil) { 354 // unset v 355 v.Set(reflect.Zero(v.Type())) 356 } 357 358 return vpt, err 359 } 360 361 // pt returns PropertyTranslator from passed reflect.Value. 362 // this function is peculiar to mercari/datastore. 363 func pt(v reflect.Value) (PropertyTranslator, error) { 364 if v.Kind() != reflect.Ptr { 365 vps, ok := v.Interface().(PropertyTranslator) 366 if ok { 367 return vps, nil 368 } 369 370 v = v.Addr() 371 } 372 373 vps, _ := v.Interface().(PropertyTranslator) 374 return vps, nil 375 }