func propertiesToProto(key *Key, props []Property) (*pb.Entity, error) { e := &pb.Entity{ Key: keyToProto(key), } indexedProps := 0 prevMultiple := make(map[string]*pb.Property) for _, p := range props { val, err := interfaceToProto(p.Value) if err != "" { return nil, fmt.Errorf("datastore: %s for a Property with Name %q", err, p.Name) } if !p.NoIndex { rVal := reflect.ValueOf(p.Value) if rVal.Kind() == reflect.Slice && rVal.Type().Elem().Kind() != reflect.Uint8 { indexedProps += rVal.Len() } else { indexedProps++ } } if indexedProps > maxIndexedProperties { return nil, errors.New("datastore: too many indexed properties") } switch v := p.Value.(type) { case string: case []byte: if len(v) > 500 && !p.NoIndex { return nil, fmt.Errorf("datastore: cannot index a Property with Name %q", p.Name) } } val.Indexed = proto.Bool(!p.NoIndex) if p.Multiple { x, ok := prevMultiple[p.Name] if !ok { x = &pb.Property{ Name: proto.String(p.Name), Value: &pb.Value{}, } prevMultiple[p.Name] = x e.Property = append(e.Property, x) } x.Value.ListValue = append(x.Value.ListValue, val) } else { e.Property = append(e.Property, &pb.Property{ Name: proto.String(p.Name), Value: val, }) } } return e, nil }
func interfaceToProto(iv interface{}) (p *pb.Value, errStr string) { val := new(pb.Value) switch v := iv.(type) { case int: val.IntegerValue = proto.Int64(int64(v)) case int32: val.IntegerValue = proto.Int64(int64(v)) case int64: val.IntegerValue = proto.Int64(v) case bool: val.BooleanValue = proto.Bool(v) case string: val.StringValue = proto.String(v) case float32: val.DoubleValue = proto.Float64(float64(v)) case float64: val.DoubleValue = proto.Float64(v) case *Key: if v != nil { val.KeyValue = keyToProto(v) } case time.Time: if v.Before(minTime) || v.After(maxTime) { return nil, fmt.Sprintf("time value out of range") } val.TimestampMicrosecondsValue = proto.Int64(toUnixMicro(v)) case []byte: val.BlobValue = v default: if iv != nil { return nil, fmt.Sprintf("invalid Value type %t", iv) } } // TODO(jbd): Support ListValue and EntityValue. // TODO(jbd): Support types whose underlying type is one of the types above. return val, "" }