mirror of https://github.com/restic/restic.git
183 lines
5.2 KiB
Go
183 lines
5.2 KiB
Go
// Copyright 2017 Google Inc. All Rights Reserved.
|
||
//
|
||
// 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"
|
||
|
||
pb "google.golang.org/genproto/googleapis/firestore/v1beta1"
|
||
|
||
"github.com/golang/protobuf/ptypes"
|
||
)
|
||
|
||
// A Precondition modifies a Firestore update or delete operation.
|
||
type Precondition interface {
|
||
// Returns the corresponding Precondition proto.
|
||
preconditionProto() (*pb.Precondition, error)
|
||
}
|
||
|
||
// Exists returns a Precondition that checks for the existence or non-existence
|
||
// of a resource before writing to it. If the check fails, the write does not occur.
|
||
func Exists(b bool) Precondition { return exists(b) }
|
||
|
||
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 { return fmt.Sprintf("Exists(%t)", e) }
|
||
|
||
// 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.
|
||
//
|
||
// Each element of fieldPaths must be a single field or a dot-separated sequence of
|
||
// fields, none of which contain the runes "˜*/[]". Use MergePaths instead for such
|
||
// paths.
|
||
func Merge(fieldPaths ...string) SetOption {
|
||
fps, err := parseDotSeparatedStrings(fieldPaths)
|
||
if err != nil {
|
||
return merge{err: err}
|
||
}
|
||
return merge{paths: fps}
|
||
}
|
||
|
||
// MergePaths 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 MergePaths(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)
|
||
}
|
||
}
|