|
- // Copyright 2017 Google LLC
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
-
- package firestore
-
- import (
- "errors"
- "fmt"
- "time"
-
- "github.com/golang/protobuf/ptypes"
- pb "google.golang.org/genproto/googleapis/firestore/v1"
- )
-
- // A Precondition modifies a Firestore update or delete operation.
- type Precondition interface {
- // Returns the corresponding Precondition proto.
- preconditionProto() (*pb.Precondition, error)
- }
-
- // Exists is a Precondition that checks for the existence of a resource before
- // writing to it. If the check fails, the write does not occur.
- var Exists Precondition
-
- func init() {
- // Initialize here so godoc doesn't show the internal value.
- Exists = exists(true)
- }
-
- type exists bool
-
- func (e exists) preconditionProto() (*pb.Precondition, error) {
- return &pb.Precondition{
- ConditionType: &pb.Precondition_Exists{bool(e)},
- }, nil
- }
-
- func (e exists) String() string {
- if e {
- return "Exists"
- }
- return "DoesNotExist"
- }
-
- // LastUpdateTime returns a Precondition that checks that a resource must exist and
- // must have last been updated at the given time. If the check fails, the write
- // does not occur.
- func LastUpdateTime(t time.Time) Precondition { return lastUpdateTime(t) }
-
- type lastUpdateTime time.Time
-
- func (u lastUpdateTime) preconditionProto() (*pb.Precondition, error) {
- ts, err := ptypes.TimestampProto(time.Time(u))
- if err != nil {
- return nil, err
- }
- return &pb.Precondition{
- ConditionType: &pb.Precondition_UpdateTime{ts},
- }, nil
- }
-
- func (u lastUpdateTime) String() string { return fmt.Sprintf("LastUpdateTime(%s)", time.Time(u)) }
-
- func processPreconditionsForDelete(preconds []Precondition) (*pb.Precondition, error) {
- // At most one option permitted.
- switch len(preconds) {
- case 0:
- return nil, nil
- case 1:
- return preconds[0].preconditionProto()
- default:
- return nil, fmt.Errorf("firestore: conflicting preconditions: %+v", preconds)
- }
- }
-
- func processPreconditionsForUpdate(preconds []Precondition) (*pb.Precondition, error) {
- // At most one option permitted, and it cannot be Exists.
- switch len(preconds) {
- case 0:
- // If the user doesn't provide any options, default to Exists(true).
- return exists(true).preconditionProto()
- case 1:
- if _, ok := preconds[0].(exists); ok {
- return nil, errors.New("cannot use Exists with Update")
- }
- return preconds[0].preconditionProto()
- default:
- return nil, fmt.Errorf("firestore: conflicting preconditions: %+v", preconds)
- }
- }
-
- func processPreconditionsForVerify(preconds []Precondition) (*pb.Precondition, error) {
- // At most one option permitted.
- switch len(preconds) {
- case 0:
- return nil, nil
- case 1:
- return preconds[0].preconditionProto()
- default:
- return nil, fmt.Errorf("firestore: conflicting preconditions: %+v", preconds)
- }
- }
-
- // A SetOption modifies a Firestore set operation.
- type SetOption interface {
- fieldPaths() (fps []FieldPath, all bool, err error)
- }
-
- // MergeAll is a SetOption that causes all the field paths given in the data argument
- // to Set to be overwritten. It is not supported for struct data.
- var MergeAll SetOption = merge{all: true}
-
- // Merge returns a SetOption that causes only the given field paths to be
- // overwritten. Other fields on the existing document will be untouched. It is an
- // error if a provided field path does not refer to a value in the data passed to
- // Set.
- func Merge(fps ...FieldPath) SetOption {
- for _, fp := range fps {
- if err := fp.validate(); err != nil {
- return merge{err: err}
- }
- }
- return merge{paths: fps}
- }
-
- type merge struct {
- all bool
- paths []FieldPath
- err error
- }
-
- func (m merge) String() string {
- if m.err != nil {
- return fmt.Sprintf("<Merge error: %v>", m.err)
- }
- if m.all {
- return "MergeAll"
- }
- return fmt.Sprintf("Merge(%+v)", m.paths)
- }
-
- func (m merge) fieldPaths() (fps []FieldPath, all bool, err error) {
- if m.err != nil {
- return nil, false, m.err
- }
- if err := checkNoDupOrPrefix(m.paths); err != nil {
- return nil, false, err
- }
- if m.all {
- return nil, true, nil
- }
- return m.paths, false, nil
- }
-
- func processSetOptions(opts []SetOption) (fps []FieldPath, all bool, err error) {
- switch len(opts) {
- case 0:
- return nil, false, nil
- case 1:
- return opts[0].fieldPaths()
- default:
- return nil, false, fmt.Errorf("conflicting options: %+v", opts)
- }
- }
|