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  }