| Index: service/rawdatastore/properties.go
 | 
| diff --git a/service/rawdatastore/properties.go b/service/rawdatastore/properties.go
 | 
| deleted file mode 100644
 | 
| index 388afba8506d41c874c6e249bfd6c6c301a7f63f..0000000000000000000000000000000000000000
 | 
| --- a/service/rawdatastore/properties.go
 | 
| +++ /dev/null
 | 
| @@ -1,451 +0,0 @@
 | 
| -// Copyright 2015 The Chromium Authors. All rights reserved.
 | 
| -// Use of this source code is governed by a BSD-style license that can be
 | 
| -// found in the LICENSE file.
 | 
| -
 | 
| -package rawdatastore
 | 
| -
 | 
| -import (
 | 
| -	"errors"
 | 
| -	"fmt"
 | 
| -	"math"
 | 
| -	"reflect"
 | 
| -	"time"
 | 
| -
 | 
| -	"github.com/luci/gae/service/blobstore"
 | 
| -)
 | 
| -
 | 
| -var (
 | 
| -	minTime = time.Unix(int64(math.MinInt64)/1e6, (int64(math.MinInt64)%1e6)*1e3)
 | 
| -	maxTime = time.Unix(int64(math.MaxInt64)/1e6, (int64(math.MaxInt64)%1e6)*1e3)
 | 
| -)
 | 
| -
 | 
| -// IndexSetting indicates whether or not a Property should be indexed by the
 | 
| -// datastore.
 | 
| -type IndexSetting bool
 | 
| -
 | 
| -// ShouldIndex is the default, which is why it must assume the zero value,
 | 
| -// even though it's werid :(.
 | 
| -const (
 | 
| -	ShouldIndex IndexSetting = false
 | 
| -	NoIndex     IndexSetting = true
 | 
| -)
 | 
| -
 | 
| -func (i IndexSetting) String() string {
 | 
| -	if i {
 | 
| -		return "NoIndex"
 | 
| -	}
 | 
| -	return "ShouldIndex"
 | 
| -}
 | 
| -
 | 
| -// Property is a value plus an indicator of whether the value should be
 | 
| -// indexed. Name and Multiple are stored in the PropertyMap object.
 | 
| -type Property struct {
 | 
| -	value        interface{}
 | 
| -	indexSetting IndexSetting
 | 
| -	propType     PropertyType
 | 
| -}
 | 
| -
 | 
| -// MkProperty makes a new indexed* Property and returns it. If val is an
 | 
| -// invalid value, this panics (so don't do it). If you want to handle the error
 | 
| -// normally, use SetValue(..., ShouldIndex) instead.
 | 
| -//
 | 
| -// *indexed if val is not an unindexable type like []byte.
 | 
| -func MkProperty(val interface{}) Property {
 | 
| -	ret := Property{}
 | 
| -	if err := ret.SetValue(val, ShouldIndex); err != nil {
 | 
| -		panic(err)
 | 
| -	}
 | 
| -	return ret
 | 
| -}
 | 
| -
 | 
| -// MkPropertyNI makes a new Property (with noindex set to true), and returns
 | 
| -// it. If val is an invalid value, this panics (so don't do it). If you want to
 | 
| -// handle the error normally, use SetValue(..., NoIndex) instead.
 | 
| -func MkPropertyNI(val interface{}) Property {
 | 
| -	ret := Property{}
 | 
| -	if err := ret.SetValue(val, NoIndex); err != nil {
 | 
| -		panic(err)
 | 
| -	}
 | 
| -	return ret
 | 
| -}
 | 
| -
 | 
| -// PropertyConverter may be implemented by the pointer-to a struct field which
 | 
| -// is serialized by RawDatastore. Its ToProperty will be called on save, and
 | 
| -// it's FromProperty will be called on load (from datastore). The method may
 | 
| -// do arbitrary computation, and if it encounters an error, may return it.  This
 | 
| -// error will be a fatal error (as defined by PropertyLoadSaver) for the
 | 
| -// struct conversion.
 | 
| -//
 | 
| -// Example:
 | 
| -//   type Complex complex
 | 
| -//   func (c *Complex) ToProperty() (ret Property, err error) {
 | 
| -//     // something like:
 | 
| -//     err = ret.SetValue(fmt.Sprint(*c), true)
 | 
| -//     return
 | 
| -//   }
 | 
| -//   func (c *Complex) FromProperty(p Property) (err error) {
 | 
| -//     ... load *c from p ...
 | 
| -//   }
 | 
| -//
 | 
| -//   type MyStruct struct {
 | 
| -//     Complexity []Complex // acts like []complex, but can be serialized to DS
 | 
| -//   }
 | 
| -type PropertyConverter interface {
 | 
| -	// TODO(riannucci): Allow a convertable to return multiple values.  This is
 | 
| -	// eminently doable (as long as the single-slice restriction is kept).  It
 | 
| -	// could also cut down on the amount of reflection necessary when resolving
 | 
| -	// a path in a struct (in the struct loading routine in helper).
 | 
| -
 | 
| -	ToProperty() (Property, error)
 | 
| -	FromProperty(Property) error
 | 
| -}
 | 
| -
 | 
| -// PropertyType is a single-byte representation of the type of data contained
 | 
| -// in a Property. The specific values of this type information are chosen so
 | 
| -// that the types sort according to the order of types as sorted by the
 | 
| -// datastore.
 | 
| -type PropertyType byte
 | 
| -
 | 
| -// These constants are in the order described by
 | 
| -//   https://cloud.google.com/appengine/docs/go/datastore/entities#Go_Value_type_ordering
 | 
| -// with a slight divergence for the Int/Time split.
 | 
| -// NOTE: this enum can only occupy 7 bits, because we use the high bit to encode
 | 
| -// indexed/non-indexed. See typData.WriteBinary.
 | 
| -const (
 | 
| -	PTNull PropertyType = iota
 | 
| -	PTInt
 | 
| -
 | 
| -	// PTTime is a slight divergence from the way that datastore natively stores
 | 
| -	// time. In datastore, times and integers actually sort together
 | 
| -	// (apparently?). This is probably insane, and I don't want to add the
 | 
| -	// complexity of field 'meaning' as a sparate concept from the field's 'type'
 | 
| -	// (which is what datastore seems to do, judging from the protobufs). So if
 | 
| -	// you're here because you implemented an app which relies on time.Time and
 | 
| -	// int64 sorting together, then this is why your app acts differently in
 | 
| -	// production. My advice is to NOT DO THAT. If you really want this (and you
 | 
| -	// probably don't), you should take care of the time.Time <-> int64 conversion
 | 
| -	// in your app and just use a property type of int64 (consider using
 | 
| -	// PropertyConverter).
 | 
| -	PTTime
 | 
| -
 | 
| -	// PTBoolFalse and True are also a slight divergence, but not a semantic
 | 
| -	// one. IIUC, in datastore 'bool' is actually the type and the value is either
 | 
| -	// 0 or 1 (taking another byte to store). Since we have plenty of space in
 | 
| -	// this type byte, I just merge the value into the type for booleans. If this
 | 
| -	// becomes problematic, consider changing this to just pvBool, and then
 | 
| -	// encoding a 0 or 1 as a byte in the relevant marshalling routines.
 | 
| -	PTBoolFalse
 | 
| -	PTBoolTrue
 | 
| -
 | 
| -	PTBytes  // []byte or datastore.ByteString
 | 
| -	PTString // string or string noindex
 | 
| -	PTFloat
 | 
| -	PTGeoPoint
 | 
| -	PTKey
 | 
| -	PTBlobKey
 | 
| -
 | 
| -	PTUnknown
 | 
| -)
 | 
| -
 | 
| -func (t PropertyType) String() string {
 | 
| -	switch t {
 | 
| -	case PTNull:
 | 
| -		return "PTNull"
 | 
| -	case PTInt:
 | 
| -		return "PTInt"
 | 
| -	case PTTime:
 | 
| -		return "PTTime"
 | 
| -	case PTBoolFalse:
 | 
| -		return "PTBoolFalse"
 | 
| -	case PTBoolTrue:
 | 
| -		return "PTBoolTrue"
 | 
| -	case PTBytes:
 | 
| -		return "PTBytes"
 | 
| -	case PTString:
 | 
| -		return "PTString"
 | 
| -	case PTFloat:
 | 
| -		return "PTFloat"
 | 
| -	case PTGeoPoint:
 | 
| -		return "PTGeoPoint"
 | 
| -	case PTKey:
 | 
| -		return "PTKey"
 | 
| -	case PTBlobKey:
 | 
| -		return "PTBlobKey"
 | 
| -	default:
 | 
| -		return fmt.Sprintf("PTUnknown(%02x)", byte(t))
 | 
| -	}
 | 
| -}
 | 
| -
 | 
| -// PropertyTypeOf returns the PT* type of the given Property-compatible
 | 
| -// value v. If checkValid is true, this method will also ensure that time.Time
 | 
| -// and GeoPoint have valid values.
 | 
| -func PropertyTypeOf(v interface{}, checkValid bool) (PropertyType, error) {
 | 
| -	switch x := v.(type) {
 | 
| -	case nil:
 | 
| -		return PTNull, nil
 | 
| -	case int64:
 | 
| -		return PTInt, nil
 | 
| -	case float64:
 | 
| -		return PTFloat, nil
 | 
| -	case bool:
 | 
| -		if x {
 | 
| -			return PTBoolTrue, nil
 | 
| -		}
 | 
| -		return PTBoolFalse, nil
 | 
| -	case []byte, ByteString:
 | 
| -		return PTBytes, nil
 | 
| -	case blobstore.Key:
 | 
| -		return PTBlobKey, nil
 | 
| -	case string:
 | 
| -		return PTString, nil
 | 
| -	case Key:
 | 
| -		// TODO(riannucci): Check key for validity in its own namespace?
 | 
| -		return PTKey, nil
 | 
| -	case time.Time:
 | 
| -		err := error(nil)
 | 
| -		if checkValid && (x.Before(minTime) || x.After(maxTime)) {
 | 
| -			err = errors.New("time value out of range")
 | 
| -		}
 | 
| -		if checkValid && x.Location() != time.UTC {
 | 
| -			err = fmt.Errorf("time value has wrong Location: %s", x.Location())
 | 
| -		}
 | 
| -		return PTTime, err
 | 
| -	case GeoPoint:
 | 
| -		err := error(nil)
 | 
| -		if checkValid && !x.Valid() {
 | 
| -			err = errors.New("invalid GeoPoint value")
 | 
| -		}
 | 
| -		return PTGeoPoint, err
 | 
| -	default:
 | 
| -		return PTUnknown, fmt.Errorf("gae: Property has bad type %T", v)
 | 
| -	}
 | 
| -}
 | 
| -
 | 
| -// UpconvertUnderlyingType takes an object o, and attempts to convert it to
 | 
| -// its native datastore-compatible type. e.g. int16 will convert to int64, and
 | 
| -// `type Foo string` will convert to `string`.
 | 
| -func UpconvertUnderlyingType(o interface{}, t reflect.Type) (interface{}, reflect.Type) {
 | 
| -	v := reflect.ValueOf(o)
 | 
| -	switch t.Kind() {
 | 
| -	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | 
| -		o = v.Int()
 | 
| -		t = typeOfInt64
 | 
| -	case reflect.Bool:
 | 
| -		o = v.Bool()
 | 
| -		t = typeOfBool
 | 
| -	case reflect.String:
 | 
| -		if t != typeOfBSKey {
 | 
| -			o = v.String()
 | 
| -			t = typeOfString
 | 
| -		}
 | 
| -	case reflect.Float32, reflect.Float64:
 | 
| -		o = v.Float()
 | 
| -		t = typeOfFloat64
 | 
| -	case reflect.Slice:
 | 
| -		if t != typeOfByteString && t.Elem().Kind() == reflect.Uint8 {
 | 
| -			o = v.Bytes()
 | 
| -			t = typeOfByteSlice
 | 
| -		}
 | 
| -	case reflect.Struct:
 | 
| -		if t == typeOfTime {
 | 
| -			// time in a Property can only hold microseconds
 | 
| -			o = v.Interface().(time.Time).Round(time.Microsecond)
 | 
| -		}
 | 
| -	}
 | 
| -	return o, t
 | 
| -}
 | 
| -
 | 
| -// Value returns the current value held by this property. It's guaranteed to
 | 
| -// be a valid value type (i.e. `p.SetValue(p.Value(), true)` will never return
 | 
| -// an error).
 | 
| -func (p *Property) Value() interface{} { return p.value }
 | 
| -
 | 
| -// IndexSetting says weather or not the datastore should create indicies for
 | 
| -// this value.
 | 
| -func (p *Property) IndexSetting() IndexSetting { return p.indexSetting }
 | 
| -
 | 
| -// Type is the PT* type of the data contained in Value().
 | 
| -func (p *Property) Type() PropertyType { return p.propType }
 | 
| -
 | 
| -// SetValue sets the Value field of a Property, and ensures that its value
 | 
| -// conforms to the permissible types. That way, you're guaranteed that if you
 | 
| -// have a Property, its value is valid.
 | 
| -//
 | 
| -// value is the property value. The valid types are:
 | 
| -//	- int64
 | 
| -//	- bool
 | 
| -//	- string
 | 
| -//	- float64
 | 
| -//	- ByteString
 | 
| -//	- Key
 | 
| -//	- time.Time
 | 
| -//	- blobstore.Key
 | 
| -//	- GeoPoint
 | 
| -//	- []byte (up to 1 megabyte in length)
 | 
| -// This set is smaller than the set of valid struct field types that the
 | 
| -// datastore can load and save. A Property Value cannot be a slice (apart
 | 
| -// from []byte); use multiple Properties instead. Also, a Value's type
 | 
| -// must be explicitly on the list above; it is not sufficient for the
 | 
| -// underlying type to be on that list. For example, a Value of "type
 | 
| -// myInt64 int64" is invalid. Smaller-width integers and floats are also
 | 
| -// invalid. Again, this is more restrictive than the set of valid struct
 | 
| -// field types.
 | 
| -//
 | 
| -// A value may also be the nil interface value; this is equivalent to
 | 
| -// Python's None but not directly representable by a Go struct. Loading
 | 
| -// a nil-valued property into a struct will set that field to the zero
 | 
| -// value.
 | 
| -func (p *Property) SetValue(value interface{}, is IndexSetting) (err error) {
 | 
| -	t := reflect.Type(nil)
 | 
| -	pt := PTNull
 | 
| -	if value != nil {
 | 
| -		t = reflect.TypeOf(value)
 | 
| -		value, t = UpconvertUnderlyingType(value, t)
 | 
| -		if pt, err = PropertyTypeOf(value, true); err != nil {
 | 
| -			return
 | 
| -		}
 | 
| -	}
 | 
| -	p.propType = pt
 | 
| -	p.value = value
 | 
| -	p.indexSetting = is
 | 
| -	if t == typeOfByteSlice {
 | 
| -		p.indexSetting = NoIndex
 | 
| -	}
 | 
| -	return
 | 
| -}
 | 
| -
 | 
| -// PropertyLoadSaver may be implemented by a user type, and RawDatastore will
 | 
| -// use this interface to serialize the type instead of trying to automatically
 | 
| -// create a serialization codec for it with helper.GetPLS.
 | 
| -type PropertyLoadSaver interface {
 | 
| -	// Load takes the values from the given map and attempts to save them into
 | 
| -	// the underlying object (usually a struct or a PropertyMap). If a fatal
 | 
| -	// error occurs, it's returned via error. If non-fatal conversion errors
 | 
| -	// occur, error will be a MultiError containing one or more ErrFieldMismatch
 | 
| -	// objects.
 | 
| -	Load(PropertyMap) error
 | 
| -
 | 
| -	// Save returns the current property as a PropertyMap. if withMeta is true,
 | 
| -	// then the PropertyMap contains all the metadata (e.g. '$meta' fields)
 | 
| -	// which was held by this PropertyLoadSaver.
 | 
| -	Save(withMeta bool) (PropertyMap, error)
 | 
| -
 | 
| -	// GetMeta will get information about the field which has the struct tag in
 | 
| -	// the form of `gae:"$<key>[,<value>]?"`.
 | 
| -	//
 | 
| -	// string and int64 fields will return the <value> in the struct tag,
 | 
| -	// converted to the appropriate type, if the field has the zero value.
 | 
| -	//
 | 
| -	// Example:
 | 
| -	//   type MyStruct struct {
 | 
| -	//     CoolField int64 `gae:"$id,1"`
 | 
| -	//   }
 | 
| -	//   val, err := helper.GetPLS(&MyStruct{}).GetMeta("id")
 | 
| -	//   // val == 1
 | 
| -	//   // err == nil
 | 
| -	//
 | 
| -	//   val, err := helper.GetPLS(&MyStruct{10}).GetMeta("id")
 | 
| -	//   // val == 10
 | 
| -	//   // err == nil
 | 
| -	//
 | 
| -	// Struct fields of type Toggle (which is an Auto/On/Off) allow you to
 | 
| -	// specify a value of 'true' or 'false' for the default value of the struct
 | 
| -	// tag, and GetMeta will return the combined value as a regular boolean true
 | 
| -	// or false value. If a field is Toggle, a <value> MUST be specified.
 | 
| -	//
 | 
| -	// Example:
 | 
| -	//   type MyStruct struct {
 | 
| -	//     TFlag Toggle `gae:"$flag1,true"`  // defaults to true
 | 
| -	//     FFlag Toggle `gae:"$flag2,false"` // defaults to false
 | 
| -	//     // BadFlag  Toggle `gae:"$flag3"` // ILLEGAL
 | 
| -	//   }
 | 
| -	GetMeta(key string) (interface{}, error)
 | 
| -
 | 
| -	// SetMeta allows you to set the current value of the meta-keyed field.
 | 
| -	SetMeta(key string, val interface{}) error
 | 
| -
 | 
| -	// Problem indicates that this PLS has a fatal problem. Usually this is
 | 
| -	// set when the underlying struct has recursion, invalid field types, nested
 | 
| -	// slices, etc.
 | 
| -	Problem() error
 | 
| -}
 | 
| -
 | 
| -// PropertyMap represents the contents of a datastore entity in a generic way.
 | 
| -// It maps from property name to a list of property values which correspond to
 | 
| -// that property name. It is the spiritual successor to PropertyList from the
 | 
| -// original SDK.
 | 
| -//
 | 
| -// PropertyMap may contain "meta" values, which are keyed with a '$' prefix.
 | 
| -// Technically the datastore allows arbitrary property names, but all of the
 | 
| -// SDKs go out of their way to try to make all property names valid programming
 | 
| -// language tokens. Special values must correspond to a single Property...
 | 
| -// corresponding to 0 is equivalent to unset, and corresponding to >1 is an
 | 
| -// error. So:
 | 
| -//
 | 
| -//   {
 | 
| -//     "$id": {MkProperty(1)}, // GetProperty("id") -> 1, nil
 | 
| -//     "$foo": {}, // GetProperty("foo") -> nil, ErrMetaFieldUnset
 | 
| -//     // GetProperty("bar") -> nil, ErrMetaFieldUnset
 | 
| -//     "$meep": {
 | 
| -//       MkProperty("hi"),
 | 
| -//       MkProperty("there")}, // GetProperty("meep") -> nil, error!
 | 
| -//   }
 | 
| -//
 | 
| -// Additionally, Save returns a copy of the map with the meta keys omitted (e.g.
 | 
| -// these keys are not going to be serialized to the datastore).
 | 
| -type PropertyMap map[string][]Property
 | 
| -
 | 
| -var _ PropertyLoadSaver = PropertyMap(nil)
 | 
| -
 | 
| -// Load implements PropertyLoadSaver.Load
 | 
| -func (pm PropertyMap) Load(props PropertyMap) error {
 | 
| -	for k, v := range props {
 | 
| -		pm[k] = append(pm[k], v...)
 | 
| -	}
 | 
| -	return nil
 | 
| -}
 | 
| -
 | 
| -// Save implements PropertyLoadSaver.Save by returning a copy of the
 | 
| -// current map data.
 | 
| -func (pm PropertyMap) Save(withMeta bool) (PropertyMap, error) {
 | 
| -	if len(pm) == 0 {
 | 
| -		return PropertyMap{}, nil
 | 
| -	}
 | 
| -	ret := make(PropertyMap, len(pm))
 | 
| -	for k, v := range pm {
 | 
| -		if withMeta || len(k) == 0 || k[0] != '$' {
 | 
| -			ret[k] = append(ret[k], v...)
 | 
| -		}
 | 
| -	}
 | 
| -	return ret, nil
 | 
| -}
 | 
| -
 | 
| -// GetMeta implements PropertyLoadSaver.GetMeta, and returns the current value
 | 
| -// associated with the metadata key. It may return ErrMetaFieldUnset if the
 | 
| -// key doesn't exist.
 | 
| -func (pm PropertyMap) GetMeta(key string) (interface{}, error) {
 | 
| -	v, ok := pm["$"+key]
 | 
| -	if !ok || len(v) == 0 {
 | 
| -		return nil, ErrMetaFieldUnset
 | 
| -	}
 | 
| -	if len(v) > 1 {
 | 
| -		return nil, errors.New("gae: too many values for Meta key")
 | 
| -	}
 | 
| -	return v[0].Value(), nil
 | 
| -}
 | 
| -
 | 
| -// SetMeta implements PropertyLoadSaver.SetMeta. It will only return an error
 | 
| -// if `val` has an invalid type (e.g. not one supported by Property).
 | 
| -func (pm PropertyMap) SetMeta(key string, val interface{}) error {
 | 
| -	prop := Property{}
 | 
| -	if err := prop.SetValue(val, NoIndex); err != nil {
 | 
| -		return err
 | 
| -	}
 | 
| -	pm["$"+key] = []Property{prop}
 | 
| -	return nil
 | 
| -}
 | 
| -
 | 
| -// Problem implements PropertyLoadSaver.Problem. It ALWAYS returns nil.
 | 
| -func (pm PropertyMap) Problem() error {
 | 
| -	return nil
 | 
| -}
 | 
| 
 |