github.com/thiagoyeds/go-cloud@v0.26.0/docstore/gcpfirestore/codec.go (about) 1 // Copyright 2019 The Go Cloud Development Kit Authors 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 // https://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 15 package gcpfirestore 16 17 // Encoding and decoding between supported docstore types and Firestore protos. 18 19 import ( 20 "errors" 21 "fmt" 22 "path" 23 "reflect" 24 "time" 25 26 "gocloud.dev/docstore/driver" 27 pb "google.golang.org/genproto/googleapis/firestore/v1" 28 "google.golang.org/genproto/googleapis/type/latlng" 29 tspb "google.golang.org/protobuf/types/known/timestamppb" 30 ) 31 32 // encodeDoc encodes a driver.Document into Firestore's representation. 33 // A Firestore document (*pb.Document) is just a Go map from strings to *pb.Values. 34 func encodeDoc(doc driver.Document, nameField string) (*pb.Document, error) { 35 var e encoder 36 if err := doc.Encode(&e); err != nil { 37 return nil, err 38 } 39 fields := e.pv.GetMapValue().Fields 40 // Do not put the name field in the document itself. 41 if nameField != "" { 42 delete(fields, nameField) 43 } 44 return &pb.Document{Fields: fields}, nil 45 } 46 47 // encodeValue encodes a Go value as a Firestore Value. 48 // The Firestore proto definition for Value is a oneof of various types, 49 // including basic types like string as well as lists and maps. 50 func encodeValue(x interface{}) (*pb.Value, error) { 51 var e encoder 52 if err := driver.Encode(reflect.ValueOf(x), &e); err != nil { 53 return nil, err 54 } 55 return e.pv, nil 56 } 57 58 // encoder implements driver.Encoder. Its job is to encode a single Firestore value. 59 type encoder struct { 60 pv *pb.Value 61 } 62 63 var nullValue = &pb.Value{ValueType: &pb.Value_NullValue{}} 64 65 func (e *encoder) EncodeNil() { e.pv = nullValue } 66 func (e *encoder) EncodeBool(x bool) { e.pv = &pb.Value{ValueType: &pb.Value_BooleanValue{x}} } 67 func (e *encoder) EncodeInt(x int64) { e.pv = &pb.Value{ValueType: &pb.Value_IntegerValue{x}} } 68 func (e *encoder) EncodeUint(x uint64) { e.pv = &pb.Value{ValueType: &pb.Value_IntegerValue{int64(x)}} } 69 func (e *encoder) EncodeBytes(x []byte) { e.pv = &pb.Value{ValueType: &pb.Value_BytesValue{x}} } 70 func (e *encoder) EncodeFloat(x float64) { e.pv = floatval(x) } 71 func (e *encoder) EncodeString(x string) { e.pv = &pb.Value{ValueType: &pb.Value_StringValue{x}} } 72 73 func (e *encoder) ListIndex(int) { panic("impossible") } 74 func (e *encoder) MapKey(string) { panic("impossible") } 75 76 func (e *encoder) EncodeList(n int) driver.Encoder { 77 s := make([]*pb.Value, n) 78 e.pv = &pb.Value{ValueType: &pb.Value_ArrayValue{&pb.ArrayValue{Values: s}}} 79 return &listEncoder{s: s} 80 } 81 82 func (e *encoder) EncodeMap(n int) driver.Encoder { 83 m := make(map[string]*pb.Value, n) 84 e.pv = &pb.Value{ValueType: &pb.Value_MapValue{&pb.MapValue{Fields: m}}} 85 return &mapEncoder{m: m} 86 } 87 88 var ( 89 typeOfGoTime = reflect.TypeOf(time.Time{}) 90 typeOfProtoTimestamp = reflect.TypeOf((*tspb.Timestamp)(nil)) 91 typeOfLatLng = reflect.TypeOf((*latlng.LatLng)(nil)) 92 ) 93 94 // Encode time.Time, latlng.LatLng, and ts.Timestamp specially, because the Go Firestore 95 // client does. 96 func (e *encoder) EncodeSpecial(v reflect.Value) (bool, error) { 97 switch v.Type() { 98 case typeOfGoTime: 99 ts := tspb.New(v.Interface().(time.Time)) 100 e.pv = &pb.Value{ValueType: &pb.Value_TimestampValue{ts}} 101 return true, nil 102 case typeOfProtoTimestamp: 103 if v.IsNil() { 104 e.pv = nullValue 105 } else { 106 e.pv = &pb.Value{ValueType: &pb.Value_TimestampValue{v.Interface().(*tspb.Timestamp)}} 107 } 108 return true, nil 109 case typeOfLatLng: 110 if v.IsNil() { 111 e.pv = nullValue 112 } else { 113 e.pv = &pb.Value{ValueType: &pb.Value_GeoPointValue{v.Interface().(*latlng.LatLng)}} 114 } 115 return true, nil 116 default: 117 return false, nil 118 } 119 } 120 121 type listEncoder struct { 122 s []*pb.Value 123 encoder 124 } 125 126 func (e *listEncoder) ListIndex(i int) { e.s[i] = e.pv } 127 128 type mapEncoder struct { 129 m map[string]*pb.Value 130 encoder 131 } 132 133 func (e *mapEncoder) MapKey(k string) { e.m[k] = e.pv } 134 135 func floatval(x float64) *pb.Value { return &pb.Value{ValueType: &pb.Value_DoubleValue{x}} } 136 137 //////////////////////////////////////////////////////////////// 138 139 // decodeDoc decodes a Firestore document into a driver.Document. 140 func decodeDoc(pdoc *pb.Document, ddoc driver.Document, nameField, revField string) error { 141 if pdoc.Fields == nil { 142 pdoc.Fields = map[string]*pb.Value{} 143 } 144 if nameField != "" { 145 pdoc.Fields[nameField] = &pb.Value{ValueType: &pb.Value_StringValue{StringValue: path.Base(pdoc.Name)}} 146 } 147 mv := &pb.Value{ValueType: &pb.Value_MapValue{&pb.MapValue{Fields: pdoc.Fields}}} 148 if err := ddoc.Decode(decoder{mv}); err != nil { 149 return err 150 } 151 // Set the revision field in the document, if it exists, to the update time. 152 if ddoc.HasField(revField) && pdoc.UpdateTime != nil { 153 return ddoc.SetField(revField, pdoc.UpdateTime) 154 } 155 return nil 156 } 157 158 type decoder struct { 159 pv *pb.Value 160 } 161 162 func (d decoder) String() string { // for debugging 163 return fmt.Sprint(d.pv) 164 } 165 166 func (d decoder) AsNull() bool { 167 _, ok := d.pv.ValueType.(*pb.Value_NullValue) 168 return ok 169 } 170 171 func (d decoder) AsBool() (bool, bool) { 172 if b, ok := d.pv.ValueType.(*pb.Value_BooleanValue); ok { 173 return b.BooleanValue, true 174 } 175 return false, false 176 } 177 178 func (d decoder) AsString() (string, bool) { 179 if s, ok := d.pv.ValueType.(*pb.Value_StringValue); ok { 180 return s.StringValue, true 181 } 182 return "", false 183 } 184 185 func (d decoder) AsInt() (int64, bool) { 186 if i, ok := d.pv.ValueType.(*pb.Value_IntegerValue); ok { 187 return i.IntegerValue, true 188 } 189 return 0, false 190 } 191 192 func (d decoder) AsUint() (uint64, bool) { 193 if i, ok := d.pv.ValueType.(*pb.Value_IntegerValue); ok { 194 return uint64(i.IntegerValue), true 195 } 196 return 0, false 197 } 198 199 func (d decoder) AsFloat() (float64, bool) { 200 if f, ok := d.pv.ValueType.(*pb.Value_DoubleValue); ok { 201 return f.DoubleValue, true 202 } 203 return 0, false 204 } 205 206 func (d decoder) AsBytes() ([]byte, bool) { 207 if bs, ok := d.pv.ValueType.(*pb.Value_BytesValue); ok { 208 return bs.BytesValue, true 209 } 210 return nil, false 211 } 212 213 // AsInterface decodes the value in d into the most appropriate Go type. 214 func (d decoder) AsInterface() (interface{}, error) { 215 return decodeValue(d.pv) 216 } 217 218 func decodeValue(v *pb.Value) (interface{}, error) { 219 switch v := v.ValueType.(type) { 220 case *pb.Value_NullValue: 221 return nil, nil 222 case *pb.Value_BooleanValue: 223 return v.BooleanValue, nil 224 case *pb.Value_IntegerValue: 225 return v.IntegerValue, nil 226 case *pb.Value_DoubleValue: 227 return v.DoubleValue, nil 228 case *pb.Value_StringValue: 229 return v.StringValue, nil 230 case *pb.Value_BytesValue: 231 return v.BytesValue, nil 232 case *pb.Value_TimestampValue: 233 // Return TimestampValue as time.Time. 234 return v.TimestampValue.AsTime(), nil 235 case *pb.Value_ReferenceValue: 236 // TODO(jba): support references 237 return nil, errors.New("references are not currently supported") 238 case *pb.Value_GeoPointValue: 239 // Return GeoPointValue as *latlng.LatLng. 240 return v.GeoPointValue, nil 241 case *pb.Value_ArrayValue: 242 s := make([]interface{}, len(v.ArrayValue.Values)) 243 for i, pv := range v.ArrayValue.Values { 244 e, err := decodeValue(pv) 245 if err != nil { 246 return nil, err 247 } 248 s[i] = e 249 } 250 return s, nil 251 case *pb.Value_MapValue: 252 m := make(map[string]interface{}, len(v.MapValue.Fields)) 253 for k, pv := range v.MapValue.Fields { 254 e, err := decodeValue(pv) 255 if err != nil { 256 return nil, err 257 } 258 m[k] = e 259 } 260 return m, nil 261 } 262 return nil, fmt.Errorf("unknown firestore value type %T", v) 263 } 264 265 func (d decoder) ListLen() (int, bool) { 266 a := d.pv.GetArrayValue() 267 if a == nil { 268 return 0, false 269 } 270 return len(a.Values), true 271 } 272 273 func (d decoder) DecodeList(f func(int, driver.Decoder) bool) { 274 for i, e := range d.pv.GetArrayValue().Values { 275 if !f(i, decoder{e}) { 276 return 277 } 278 } 279 } 280 func (d decoder) MapLen() (int, bool) { 281 m := d.pv.GetMapValue() 282 if m == nil { 283 return 0, false 284 } 285 return len(m.Fields), true 286 } 287 func (d decoder) DecodeMap(f func(string, driver.Decoder, bool) bool) { 288 for k, v := range d.pv.GetMapValue().Fields { 289 if !f(k, decoder{v}, true) { 290 return 291 } 292 } 293 } 294 295 func (d decoder) AsSpecial(v reflect.Value) (bool, interface{}, error) { 296 switch v.Type() { 297 case typeOfGoTime: 298 if ts, ok := d.pv.ValueType.(*pb.Value_TimestampValue); ok { 299 if ts.TimestampValue == nil { 300 return true, time.Time{}, nil 301 } 302 return true, ts.TimestampValue.AsTime(), nil 303 } 304 return true, nil, fmt.Errorf("expected TimestampValue for time.Time, got %+v", d.pv.ValueType) 305 case typeOfProtoTimestamp: 306 if ts, ok := d.pv.ValueType.(*pb.Value_TimestampValue); ok { 307 return true, ts.TimestampValue, nil 308 } 309 return true, nil, fmt.Errorf("expected TimestampValue for *ts.Timestamp, got %+v", d.pv.ValueType) 310 311 case typeOfLatLng: 312 if ll, ok := d.pv.ValueType.(*pb.Value_GeoPointValue); ok { 313 return true, ll.GeoPointValue, nil 314 } 315 return true, nil, fmt.Errorf("expected GeoPointValue for *latlng.LatLng, got %+v", d.pv.ValueType) 316 317 default: 318 return false, nil, nil 319 } 320 }