peridot/vendor/github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute/decode.go

789 lines
21 KiB
Go
Raw Normal View History

2022-07-07 20:11:50 +00:00
package dynamodbattribute
import (
"encoding/base64"
"fmt"
"reflect"
"strconv"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/dynamodb"
)
// An Unmarshaler is an interface to provide custom unmarshaling of
// AttributeValues. Use this to provide custom logic determining
// how AttributeValues should be unmarshaled.
//
2022-11-04 02:21:49 +00:00
// type ExampleUnmarshaler struct {
// Value int
// }
2022-07-07 20:11:50 +00:00
//
2022-11-04 02:21:49 +00:00
// func (u *ExampleUnmarshaler) UnmarshalDynamoDBAttributeValue(av *dynamodb.AttributeValue) error {
// if av.N == nil {
// return nil
// }
2022-07-07 20:11:50 +00:00
//
2022-11-04 02:21:49 +00:00
// n, err := strconv.ParseInt(*av.N, 10, 0)
// if err != nil {
// return err
// }
//
// u.Value = int(n)
// return nil
// }
2022-07-07 20:11:50 +00:00
type Unmarshaler interface {
UnmarshalDynamoDBAttributeValue(*dynamodb.AttributeValue) error
}
// Unmarshal will unmarshal DynamoDB AttributeValues to Go value types.
// Both generic interface{} and concrete types are valid unmarshal
// destination types.
//
// Unmarshal will allocate maps, slices, and pointers as needed to
// unmarshal the AttributeValue into the provided type value.
//
// When unmarshaling AttributeValues into structs Unmarshal matches
// the field names of the struct to the AttributeValue Map keys.
// Initially it will look for exact field name matching, but will
// fall back to case insensitive if not exact match is found.
//
// With the exception of omitempty, omitemptyelem, binaryset, numberset
// and stringset all struct tags used by Marshal are also used by
// Unmarshal.
//
// When decoding AttributeValues to interfaces Unmarshal will use the
// following types.
//
2022-11-04 02:21:49 +00:00
// []byte, AV Binary (B)
// [][]byte, AV Binary Set (BS)
// bool, AV Boolean (BOOL)
// []interface{}, AV List (L)
// map[string]interface{}, AV Map (M)
// float64, AV Number (N)
// Number, AV Number (N) with UseNumber set
// []float64, AV Number Set (NS)
// []Number, AV Number Set (NS) with UseNumber set
// string, AV String (S)
// []string, AV String Set (SS)
2022-07-07 20:11:50 +00:00
//
// If the Decoder option, UseNumber is set numbers will be unmarshaled
// as Number values instead of float64. Use this to maintain the original
// string formating of the number as it was represented in the AttributeValue.
// In addition provides additional opportunities to parse the number
// string based on individual use cases.
//
// When unmarshaling any error that occurs will halt the unmarshal
// and return the error.
//
// The output value provided must be a non-nil pointer
func Unmarshal(av *dynamodb.AttributeValue, out interface{}) error {
return NewDecoder().Decode(av, out)
}
// UnmarshalMap is an alias for Unmarshal which unmarshals from
// a map of AttributeValues.
//
// The output value provided must be a non-nil pointer
func UnmarshalMap(m map[string]*dynamodb.AttributeValue, out interface{}) error {
return NewDecoder().Decode(&dynamodb.AttributeValue{M: m}, out)
}
// UnmarshalList is an alias for Unmarshal func which unmarshals
// a slice of AttributeValues.
//
// The output value provided must be a non-nil pointer
func UnmarshalList(l []*dynamodb.AttributeValue, out interface{}) error {
return NewDecoder().Decode(&dynamodb.AttributeValue{L: l}, out)
}
// UnmarshalListOfMaps is an alias for Unmarshal func which unmarshals a
// slice of maps of attribute values.
//
// This is useful for when you need to unmarshal the Items from a DynamoDB
// Query API call.
//
// The output value provided must be a non-nil pointer
func UnmarshalListOfMaps(l []map[string]*dynamodb.AttributeValue, out interface{}) error {
items := make([]*dynamodb.AttributeValue, len(l))
for i, m := range l {
items[i] = &dynamodb.AttributeValue{M: m}
}
return UnmarshalList(items, out)
}
// A Decoder provides unmarshaling AttributeValues to Go value types.
type Decoder struct {
MarshalOptions
// Instructs the decoder to decode AttributeValue Numbers as
// Number type instead of float64 when the destination type
// is interface{}. Similar to encoding/json.Number
UseNumber bool
}
// NewDecoder creates a new Decoder with default configuration. Use
// the `opts` functional options to override the default configuration.
func NewDecoder(opts ...func(*Decoder)) *Decoder {
d := &Decoder{
MarshalOptions: MarshalOptions{
SupportJSONTags: true,
},
}
for _, o := range opts {
o(d)
}
return d
}
// Decode will unmarshal an AttributeValue into a Go value type. An error
// will be return if the decoder is unable to unmarshal the AttributeValue
// to the provide Go value type.
//
// The output value provided must be a non-nil pointer
func (d *Decoder) Decode(av *dynamodb.AttributeValue, out interface{}, opts ...func(*Decoder)) error {
v := reflect.ValueOf(out)
if v.Kind() != reflect.Ptr || v.IsNil() || !v.IsValid() {
return &InvalidUnmarshalError{Type: reflect.TypeOf(out)}
}
return d.decode(av, v, tag{})
}
var stringInterfaceMapType = reflect.TypeOf(map[string]interface{}(nil))
var byteSliceType = reflect.TypeOf([]byte(nil))
var byteSliceSlicetype = reflect.TypeOf([][]byte(nil))
var numberType = reflect.TypeOf(Number(""))
var timeType = reflect.TypeOf(time.Time{})
var ptrStringType = reflect.TypeOf(aws.String(""))
func (d *Decoder) decode(av *dynamodb.AttributeValue, v reflect.Value, fieldTag tag) error {
var u Unmarshaler
if av == nil || av.NULL != nil {
u, v = indirect(v, true)
if u != nil {
return u.UnmarshalDynamoDBAttributeValue(av)
}
return d.decodeNull(v)
}
u, v = indirect(v, false)
if u != nil {
return u.UnmarshalDynamoDBAttributeValue(av)
}
switch {
case av.B != nil:
return d.decodeBinary(av.B, v)
case av.BOOL != nil:
return d.decodeBool(av.BOOL, v)
case av.BS != nil:
return d.decodeBinarySet(av.BS, v)
case av.L != nil:
return d.decodeList(av.L, v)
case av.M != nil:
return d.decodeMap(av.M, v)
case av.N != nil:
return d.decodeNumber(av.N, v, fieldTag)
case av.NS != nil:
return d.decodeNumberSet(av.NS, v)
case av.S != nil:
return d.decodeString(av.S, v, fieldTag)
case av.SS != nil:
return d.decodeStringSet(av.SS, v)
}
return nil
}
func (d *Decoder) decodeBinary(b []byte, v reflect.Value) error {
if v.Kind() == reflect.Interface {
buf := make([]byte, len(b))
copy(buf, b)
v.Set(reflect.ValueOf(buf))
return nil
}
if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
return &UnmarshalTypeError{Value: "binary", Type: v.Type()}
}
if v.Type() == byteSliceType {
// Optimization for []byte types
if v.IsNil() || v.Cap() < len(b) {
v.Set(reflect.MakeSlice(byteSliceType, len(b), len(b)))
} else if v.Len() != len(b) {
v.SetLen(len(b))
}
copy(v.Interface().([]byte), b)
return nil
}
switch v.Type().Elem().Kind() {
case reflect.Uint8:
// Fallback to reflection copy for type aliased of []byte type
if v.Kind() != reflect.Array && (v.IsNil() || v.Cap() < len(b)) {
v.Set(reflect.MakeSlice(v.Type(), len(b), len(b)))
} else if v.Len() != len(b) {
v.SetLen(len(b))
}
for i := 0; i < len(b); i++ {
v.Index(i).SetUint(uint64(b[i]))
}
default:
if v.Kind() == reflect.Array {
switch v.Type().Elem().Kind() {
case reflect.Uint8:
reflect.Copy(v, reflect.ValueOf(b))
default:
return &UnmarshalTypeError{Value: "binary", Type: v.Type()}
}
break
}
return &UnmarshalTypeError{Value: "binary", Type: v.Type()}
}
return nil
}
func (d *Decoder) decodeBool(b *bool, v reflect.Value) error {
switch v.Kind() {
case reflect.Bool, reflect.Interface:
v.Set(reflect.ValueOf(*b).Convert(v.Type()))
default:
return &UnmarshalTypeError{Value: "bool", Type: v.Type()}
}
return nil
}
func (d *Decoder) decodeBinarySet(bs [][]byte, v reflect.Value) error {
isArray := false
switch v.Kind() {
case reflect.Slice:
// Make room for the slice elements if needed
if v.IsNil() || v.Cap() < len(bs) {
// What about if ignoring nil/empty values?
v.Set(reflect.MakeSlice(v.Type(), 0, len(bs)))
}
case reflect.Array:
// Limited to capacity of existing array.
isArray = true
case reflect.Interface:
set := make([][]byte, len(bs))
for i, b := range bs {
if err := d.decodeBinary(b, reflect.ValueOf(&set[i]).Elem()); err != nil {
return err
}
}
v.Set(reflect.ValueOf(set))
return nil
default:
return &UnmarshalTypeError{Value: "binary set", Type: v.Type()}
}
for i := 0; i < v.Cap() && i < len(bs); i++ {
if !isArray {
v.SetLen(i + 1)
}
u, elem := indirect(v.Index(i), false)
if u != nil {
return u.UnmarshalDynamoDBAttributeValue(&dynamodb.AttributeValue{BS: bs})
}
if err := d.decodeBinary(bs[i], elem); err != nil {
return err
}
}
return nil
}
func (d *Decoder) decodeNumber(n *string, v reflect.Value, fieldTag tag) error {
switch v.Kind() {
case reflect.Interface:
i, err := d.decodeNumberToInterface(n)
if err != nil {
return err
}
v.Set(reflect.ValueOf(i))
return nil
case reflect.String:
if v.Type() == numberType { // Support Number value type
v.Set(reflect.ValueOf(Number(*n)))
return nil
}
2022-11-04 02:21:49 +00:00
v.SetString(*n)
2022-07-07 20:11:50 +00:00
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
i, err := strconv.ParseInt(*n, 10, 64)
if err != nil {
return err
}
if v.OverflowInt(i) {
return &UnmarshalTypeError{
Value: fmt.Sprintf("number overflow, %s", *n),
Type: v.Type(),
}
}
v.SetInt(i)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
i, err := strconv.ParseUint(*n, 10, 64)
if err != nil {
return err
}
if v.OverflowUint(i) {
return &UnmarshalTypeError{
Value: fmt.Sprintf("number overflow, %s", *n),
Type: v.Type(),
}
}
v.SetUint(i)
case reflect.Float32, reflect.Float64:
i, err := strconv.ParseFloat(*n, 64)
if err != nil {
return err
}
if v.OverflowFloat(i) {
return &UnmarshalTypeError{
Value: fmt.Sprintf("number overflow, %s", *n),
Type: v.Type(),
}
}
v.SetFloat(i)
default:
if v.Type().ConvertibleTo(timeType) && fieldTag.AsUnixTime {
t, err := decodeUnixTime(*n)
if err != nil {
return err
}
v.Set(reflect.ValueOf(t).Convert(v.Type()))
return nil
}
return &UnmarshalTypeError{Value: "number", Type: v.Type()}
}
return nil
}
func (d *Decoder) decodeNumberToInterface(n *string) (interface{}, error) {
if d.UseNumber {
return Number(*n), nil
}
// Default to float64 for all numbers
return strconv.ParseFloat(*n, 64)
}
func (d *Decoder) decodeNumberSet(ns []*string, v reflect.Value) error {
isArray := false
switch v.Kind() {
case reflect.Slice:
// Make room for the slice elements if needed
if v.IsNil() || v.Cap() < len(ns) {
// What about if ignoring nil/empty values?
v.Set(reflect.MakeSlice(v.Type(), 0, len(ns)))
}
case reflect.Array:
// Limited to capacity of existing array.
isArray = true
case reflect.Interface:
if d.UseNumber {
set := make([]Number, len(ns))
for i, n := range ns {
if err := d.decodeNumber(n, reflect.ValueOf(&set[i]).Elem(), tag{}); err != nil {
return err
}
}
v.Set(reflect.ValueOf(set))
} else {
set := make([]float64, len(ns))
for i, n := range ns {
if err := d.decodeNumber(n, reflect.ValueOf(&set[i]).Elem(), tag{}); err != nil {
return err
}
}
v.Set(reflect.ValueOf(set))
}
return nil
default:
return &UnmarshalTypeError{Value: "number set", Type: v.Type()}
}
for i := 0; i < v.Cap() && i < len(ns); i++ {
if !isArray {
v.SetLen(i + 1)
}
u, elem := indirect(v.Index(i), false)
if u != nil {
return u.UnmarshalDynamoDBAttributeValue(&dynamodb.AttributeValue{NS: ns})
}
if err := d.decodeNumber(ns[i], elem, tag{}); err != nil {
return err
}
}
return nil
}
func (d *Decoder) decodeList(avList []*dynamodb.AttributeValue, v reflect.Value) error {
isArray := false
switch v.Kind() {
case reflect.Slice:
// Make room for the slice elements if needed
if v.IsNil() || v.Cap() < len(avList) {
// What about if ignoring nil/empty values?
v.Set(reflect.MakeSlice(v.Type(), 0, len(avList)))
}
case reflect.Array:
// Limited to capacity of existing array.
isArray = true
case reflect.Interface:
s := make([]interface{}, len(avList))
for i, av := range avList {
if err := d.decode(av, reflect.ValueOf(&s[i]).Elem(), tag{}); err != nil {
return err
}
}
v.Set(reflect.ValueOf(s))
return nil
default:
return &UnmarshalTypeError{Value: "list", Type: v.Type()}
}
// If v is not a slice, array
for i := 0; i < v.Cap() && i < len(avList); i++ {
if !isArray {
v.SetLen(i + 1)
}
if err := d.decode(avList[i], v.Index(i), tag{}); err != nil {
return err
}
}
return nil
}
func (d *Decoder) decodeMap(avMap map[string]*dynamodb.AttributeValue, v reflect.Value) error {
switch v.Kind() {
case reflect.Map:
t := v.Type()
if t.Key().Kind() != reflect.String {
return &UnmarshalTypeError{Value: "map string key", Type: t.Key()}
}
if v.IsNil() {
v.Set(reflect.MakeMap(t))
}
case reflect.Struct:
case reflect.Interface:
v.Set(reflect.MakeMap(stringInterfaceMapType))
v = v.Elem()
default:
return &UnmarshalTypeError{Value: "map", Type: v.Type()}
}
if v.Kind() == reflect.Map {
for k, av := range avMap {
key := reflect.New(v.Type().Key()).Elem()
key.SetString(k)
elem := reflect.New(v.Type().Elem()).Elem()
if err := d.decode(av, elem, tag{}); err != nil {
return err
}
v.SetMapIndex(key, elem)
}
} else if v.Kind() == reflect.Struct {
fields := unionStructFields(v.Type(), d.MarshalOptions)
for k, av := range avMap {
if f, ok := fields.FieldByName(k); ok {
fv := decoderFieldByIndex(v, f.Index)
if err := d.decode(av, fv, f.tag); err != nil {
return err
}
}
}
}
return nil
}
func (d *Decoder) decodeNull(v reflect.Value) error {
if v.IsValid() && v.CanSet() {
v.Set(reflect.Zero(v.Type()))
}
return nil
}
func (d *Decoder) decodeString(s *string, v reflect.Value, fieldTag tag) error {
if fieldTag.AsString {
return d.decodeNumber(s, v, fieldTag)
}
// To maintain backwards compatibility with ConvertFrom family of methods which
// converted strings to time.Time structs
if v.Type().ConvertibleTo(timeType) {
t, err := time.Parse(time.RFC3339, *s)
if err != nil {
return err
}
v.Set(reflect.ValueOf(t).Convert(v.Type()))
return nil
}
switch v.Kind() {
case reflect.String:
v.SetString(*s)
case reflect.Slice:
// To maintain backwards compatibility with the ConvertFrom family of methods
// which converted []byte into base64-encoded strings if the input was typed
if v.Type() == byteSliceType {
decoded, err := base64.StdEncoding.DecodeString(*s)
if err != nil {
return &UnmarshalError{Err: err, Value: "string", Type: v.Type()}
}
v.SetBytes(decoded)
}
case reflect.Interface:
// Ensure type aliasing is handled properly
v.Set(reflect.ValueOf(*s).Convert(v.Type()))
default:
return &UnmarshalTypeError{Value: "string", Type: v.Type()}
}
return nil
}
func (d *Decoder) decodeStringSet(ss []*string, v reflect.Value) error {
isArray := false
switch v.Kind() {
case reflect.Slice:
// Make room for the slice elements if needed
if v.IsNil() || v.Cap() < len(ss) {
v.Set(reflect.MakeSlice(v.Type(), 0, len(ss)))
}
case reflect.Array:
// Limited to capacity of existing array.
isArray = true
case reflect.Interface:
set := make([]string, len(ss))
for i, s := range ss {
if err := d.decodeString(s, reflect.ValueOf(&set[i]).Elem(), tag{}); err != nil {
return err
}
}
v.Set(reflect.ValueOf(set))
return nil
default:
return &UnmarshalTypeError{Value: "string set", Type: v.Type()}
}
for i := 0; i < v.Cap() && i < len(ss); i++ {
if !isArray {
v.SetLen(i + 1)
}
u, elem := indirect(v.Index(i), false)
if u != nil {
return u.UnmarshalDynamoDBAttributeValue(&dynamodb.AttributeValue{SS: ss})
}
if err := d.decodeString(ss[i], elem, tag{}); err != nil {
return err
}
}
return nil
}
func decodeUnixTime(n string) (time.Time, error) {
v, err := strconv.ParseInt(n, 10, 64)
if err != nil {
return time.Time{}, &UnmarshalError{
Err: err, Value: n, Type: timeType,
}
}
return time.Unix(v, 0), nil
}
// decoderFieldByIndex finds the field with the provided nested index, allocating
// embedded parent structs if needed
func decoderFieldByIndex(v reflect.Value, index []int) reflect.Value {
for i, x := range index {
if i > 0 && v.Kind() == reflect.Ptr && v.Type().Elem().Kind() == reflect.Struct {
if v.IsNil() {
v.Set(reflect.New(v.Type().Elem()))
}
v = v.Elem()
}
v = v.Field(x)
}
return v
}
// indirect will walk a value's interface or pointer value types. Returning
// the final value or the value a unmarshaler is defined on.
//
// Based on the enoding/json type reflect value type indirection in Go Stdlib
// https://golang.org/src/encoding/json/decode.go indirect func.
func indirect(v reflect.Value, decodingNull bool) (Unmarshaler, reflect.Value) {
if v.Kind() != reflect.Ptr && v.Type().Name() != "" && v.CanAddr() {
v = v.Addr()
}
for {
if v.Kind() == reflect.Interface && !v.IsNil() {
e := v.Elem()
if e.Kind() == reflect.Ptr && !e.IsNil() && (!decodingNull || e.Elem().Kind() == reflect.Ptr) {
v = e
continue
}
}
if v.Kind() != reflect.Ptr {
break
}
if v.Elem().Kind() != reflect.Ptr && decodingNull && v.CanSet() {
break
}
if v.IsNil() {
v.Set(reflect.New(v.Type().Elem()))
}
if v.Type().NumMethod() > 0 {
if u, ok := v.Interface().(Unmarshaler); ok {
return u, reflect.Value{}
}
}
v = v.Elem()
}
return nil, v
}
// A Number represents a Attributevalue number literal.
type Number string
// Float64 attempts to cast the number ot a float64, returning
// the result of the case or error if the case failed.
func (n Number) Float64() (float64, error) {
return strconv.ParseFloat(string(n), 64)
}
// Int64 attempts to cast the number ot a int64, returning
// the result of the case or error if the case failed.
func (n Number) Int64() (int64, error) {
return strconv.ParseInt(string(n), 10, 64)
}
// Uint64 attempts to cast the number ot a uint64, returning
// the result of the case or error if the case failed.
func (n Number) Uint64() (uint64, error) {
return strconv.ParseUint(string(n), 10, 64)
}
// String returns the raw number represented as a string
func (n Number) String() string {
return string(n)
}
type emptyOrigError struct{}
func (e emptyOrigError) OrigErr() error {
return nil
}
// An UnmarshalTypeError is an error type representing a error
// unmarshaling the AttributeValue's element to a Go value type.
// Includes details about the AttributeValue type and Go value type.
type UnmarshalTypeError struct {
emptyOrigError
Value string
Type reflect.Type
}
// Error returns the string representation of the error.
// satisfying the error interface
func (e *UnmarshalTypeError) Error() string {
return fmt.Sprintf("%s: %s", e.Code(), e.Message())
}
// Code returns the code of the error, satisfying the awserr.Error
// interface.
func (e *UnmarshalTypeError) Code() string {
return "UnmarshalTypeError"
}
// Message returns the detailed message of the error, satisfying
// the awserr.Error interface.
func (e *UnmarshalTypeError) Message() string {
return "cannot unmarshal " + e.Value + " into Go value of type " + e.Type.String()
}
// An InvalidUnmarshalError is an error type representing an invalid type
// encountered while unmarshaling a AttributeValue to a Go value type.
type InvalidUnmarshalError struct {
emptyOrigError
Type reflect.Type
}
// Error returns the string representation of the error.
// satisfying the error interface
func (e *InvalidUnmarshalError) Error() string {
return fmt.Sprintf("%s: %s", e.Code(), e.Message())
}
// Code returns the code of the error, satisfying the awserr.Error
// interface.
func (e *InvalidUnmarshalError) Code() string {
return "InvalidUnmarshalError"
}
// Message returns the detailed message of the error, satisfying
// the awserr.Error interface.
func (e *InvalidUnmarshalError) Message() string {
if e.Type == nil {
return "cannot unmarshal to nil value"
}
if e.Type.Kind() != reflect.Ptr {
return "cannot unmarshal to non-pointer value, got " + e.Type.String()
}
return "cannot unmarshal to nil value, " + e.Type.String()
}
// An UnmarshalError wraps an error that occurred while unmarshaling a DynamoDB
// AttributeValue element into a Go type. This is different from UnmarshalTypeError
// in that it wraps the underlying error that occurred.
type UnmarshalError struct {
Err error
Value string
Type reflect.Type
}
// Error returns the string representation of the error.
// satisfying the error interface.
func (e *UnmarshalError) Error() string {
return fmt.Sprintf("%s: %s\ncaused by: %v", e.Code(), e.Message(), e.Err)
}
// OrigErr returns the original error that caused this issue.
func (e UnmarshalError) OrigErr() error {
return e.Err
}
// Code returns the code of the error, satisfying the awserr.Error
// interface.
func (e *UnmarshalError) Code() string {
return "UnmarshalError"
}
// Message returns the detailed message of the error, satisfying
// the awserr.Error interface.
func (e *UnmarshalError) Message() string {
return fmt.Sprintf("cannot unmarshal %q into %s.",
e.Value, e.Type.String())
}