1// Copyright 2017 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
15package firestore
16
17import (
18	"bytes"
19	"errors"
20	"fmt"
21	"reflect"
22	"regexp"
23	"sort"
24	"strings"
25	"sync"
26
27	"cloud.google.com/go/internal/fields"
28	pb "google.golang.org/genproto/googleapis/firestore/v1"
29)
30
31// A FieldPath is a non-empty sequence of non-empty fields that reference a value.
32//
33// A FieldPath value should only be necessary if one of the field names contains
34// one of the runes ".˜*/[]". Most methods accept a simpler form of field path
35// as a string in which the individual fields are separated by dots.
36// For example,
37//   []string{"a", "b"}
38// is equivalent to the string form
39//   "a.b"
40// but
41//   []string{"*"}
42// has no equivalent string form.
43type FieldPath []string
44
45// parseDotSeparatedString constructs a FieldPath from a string that separates
46// path components with dots. Other than splitting at dots and checking for invalid
47// characters, it ignores everything else about the string,
48// including attempts to quote field path compontents. So "a.`b.c`.d" is parsed into
49// four parts, "a", "`b", "c`" and "d".
50func parseDotSeparatedString(s string) (FieldPath, error) {
51	const invalidRunes = "~*/[]"
52	if strings.ContainsAny(s, invalidRunes) {
53		return nil, fmt.Errorf("firestore: %q contains an invalid rune (one of %s)", s, invalidRunes)
54	}
55	fp := FieldPath(strings.Split(s, "."))
56	if err := fp.validate(); err != nil {
57		return nil, err
58	}
59	return fp, nil
60}
61
62func (fp1 FieldPath) equal(fp2 FieldPath) bool {
63	if len(fp1) != len(fp2) {
64		return false
65	}
66	for i, c1 := range fp1 {
67		if c1 != fp2[i] {
68			return false
69		}
70	}
71	return true
72}
73
74func (fp1 FieldPath) prefixOf(fp2 FieldPath) bool {
75	return len(fp1) <= len(fp2) && fp1.equal(fp2[:len(fp1)])
76}
77
78// Lexicographic ordering.
79func (fp1 FieldPath) less(fp2 FieldPath) bool {
80	for i := range fp1 {
81		switch {
82		case i >= len(fp2):
83			return false
84		case fp1[i] < fp2[i]:
85			return true
86		case fp1[i] > fp2[i]:
87			return false
88		}
89	}
90	// fp1 and fp2 are equal up to len(fp1).
91	return len(fp1) < len(fp2)
92}
93
94// validate checks the validity of fp and returns an error if it is invalid.
95func (fp FieldPath) validate() error {
96	if len(fp) == 0 {
97		return errors.New("firestore: empty field path")
98	}
99	for _, c := range fp {
100		if len(c) == 0 {
101			return errors.New("firestore: empty component in field path")
102		}
103	}
104	return nil
105}
106
107// with creates a new FieldPath consisting of fp followed by k.
108func (fp FieldPath) with(k string) FieldPath {
109	r := make(FieldPath, len(fp), len(fp)+1)
110	copy(r, fp)
111	return append(r, k)
112}
113
114// checkNoDupOrPrefix checks whether any FieldPath is a prefix of (or equal to)
115// another.
116// It modifies the order of FieldPaths in its argument (via sorting).
117func checkNoDupOrPrefix(fps []FieldPath) error {
118	// Sort fps lexicographically.
119	sort.Sort(byPath(fps))
120	// Check adjacent pairs for prefix.
121	for i := 1; i < len(fps); i++ {
122		if fps[i-1].prefixOf(fps[i]) {
123			return fmt.Errorf("field path %v cannot be used in the same update as %v", fps[i-1], fps[i])
124		}
125	}
126	return nil
127}
128
129type byPath []FieldPath
130
131func (b byPath) Len() int           { return len(b) }
132func (b byPath) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }
133func (b byPath) Less(i, j int) bool { return b[i].less(b[j]) }
134
135// setAtPath sets val at the location in m specified by fp, creating sub-maps as
136// needed. m must not be nil. fp is assumed to be valid.
137func setAtPath(m map[string]*pb.Value, fp FieldPath, val *pb.Value) {
138	if val == nil {
139		return
140	}
141	if len(fp) == 1 {
142		m[fp[0]] = val
143	} else {
144		v, ok := m[fp[0]]
145		if !ok {
146			v = &pb.Value{ValueType: &pb.Value_MapValue{&pb.MapValue{Fields: map[string]*pb.Value{}}}}
147			m[fp[0]] = v
148		}
149		// The type assertion below cannot fail, because setAtPath is only called
150		// with either an empty map or one filled by setAtPath itself, and the
151		// set of FieldPaths it is called with has been checked to make sure that
152		// no path is the prefix of any other.
153		setAtPath(v.GetMapValue().Fields, fp[1:], val)
154	}
155}
156
157// getAtPath gets the value in data referred to by fp. The data argument can
158// be a map or a struct.
159// Compare with valueAtPath, which does the same thing for a document.
160func getAtPath(v reflect.Value, fp FieldPath) (interface{}, error) {
161	var err error
162	for _, k := range fp {
163		v, err = getAtField(v, k)
164		if err != nil {
165			return nil, err
166		}
167	}
168	return v.Interface(), nil
169}
170
171// getAtField returns the equivalent of v[k], if v is a map, or v.k if v is a struct.
172func getAtField(v reflect.Value, k string) (reflect.Value, error) {
173	switch v.Kind() {
174	case reflect.Map:
175		if r := v.MapIndex(reflect.ValueOf(k)); r.IsValid() {
176			return r, nil
177		}
178
179	case reflect.Struct:
180		fm, err := fieldMap(v.Type())
181		if err != nil {
182			return reflect.Value{}, err
183		}
184		if f, ok := fm[k]; ok {
185			return v.FieldByIndex(f.Index), nil
186		}
187
188	case reflect.Interface:
189		return getAtField(v.Elem(), k)
190
191	case reflect.Ptr:
192		return getAtField(v.Elem(), k)
193	}
194	return reflect.Value{}, fmt.Errorf("firestore: no field %q for value %#v", k, v)
195}
196
197// fieldMapCache holds maps from from Firestore field name to struct field,
198// keyed by struct type.
199var fieldMapCache sync.Map
200
201func fieldMap(t reflect.Type) (map[string]fields.Field, error) {
202	x, ok := fieldMapCache.Load(t)
203	if !ok {
204		fieldList, err := fieldCache.Fields(t)
205		if err != nil {
206			x = err
207		} else {
208			m := map[string]fields.Field{}
209			for _, f := range fieldList {
210				m[f.Name] = f
211			}
212			x = m
213		}
214		fieldMapCache.Store(t, x)
215	}
216	if err, ok := x.(error); ok {
217		return nil, err
218	}
219	return x.(map[string]fields.Field), nil
220}
221
222// toServiceFieldPath converts fp the form required by the Firestore service.
223// It assumes fp has been validated.
224func (fp FieldPath) toServiceFieldPath() string {
225	cs := make([]string, len(fp))
226	for i, c := range fp {
227		cs[i] = toServiceFieldPathComponent(c)
228	}
229	return strings.Join(cs, ".")
230}
231
232func toServiceFieldPaths(fps []FieldPath) []string {
233	var sfps []string
234	for _, fp := range fps {
235		sfps = append(sfps, fp.toServiceFieldPath())
236	}
237	return sfps
238}
239
240// Google SQL syntax for an unquoted field.
241var unquotedFieldRegexp = regexp.MustCompile("^[A-Za-z_][A-Za-z_0-9]*$")
242
243// toServiceFieldPathComponent returns a string that represents key and is a valid
244// field path component.
245func toServiceFieldPathComponent(key string) string {
246	if unquotedFieldRegexp.MatchString(key) {
247		return key
248	}
249	var buf bytes.Buffer
250	buf.WriteRune('`')
251	for _, r := range key {
252		if r == '`' || r == '\\' {
253			buf.WriteRune('\\')
254		}
255		buf.WriteRune(r)
256	}
257	buf.WriteRune('`')
258	return buf.String()
259}
260