document.go raw

   1  package document
   2  
   3  import (
   4  	"fmt"
   5  	"math/big"
   6  	"strconv"
   7  )
   8  
   9  // Marshaler is an interface for a type that marshals a document to its protocol-specific byte representation and
  10  // returns the resulting bytes. A non-nil error will be returned if an error is encountered during marshaling.
  11  //
  12  // Marshal supports basic scalars (int,uint,float,bool,string), big.Int, and big.Float, maps, slices, and structs.
  13  // Anonymous nested types are flattened based on Go anonymous type visibility.
  14  //
  15  // When defining struct types. the `document` struct tag can be used to control how the value will be
  16  // marshaled into the resulting protocol document.
  17  //
  18  //		// Field is ignored
  19  //		Field int `document:"-"`
  20  //
  21  //		// Field object of key "myName"
  22  //		Field int `document:"myName"`
  23  //
  24  //		// Field object key of key "myName", and
  25  //		// Field is omitted if the field is a zero value for the type.
  26  //		Field int `document:"myName,omitempty"`
  27  //
  28  //		// Field object key of "Field", and
  29  //		// Field is omitted if the field is a zero value for the type.
  30  //		Field int `document:",omitempty"`
  31  //
  32  // All struct fields, including anonymous fields, are marshaled unless the
  33  // any of the following conditions are meet.
  34  //
  35  //		- the field is not exported
  36  //		- document field tag is "-"
  37  //		- document field tag specifies "omitempty", and is a zero value.
  38  //
  39  // Pointer and interface values are encoded as the value pointed to or
  40  // contained in the interface. A nil value encodes as a null
  41  // value unless `omitempty` struct tag is provided.
  42  //
  43  // Channel, complex, and function values are not encoded and will be skipped
  44  // when walking the value to be marshaled.
  45  //
  46  // time.Time is not supported and will cause the Marshaler to return an error. These values should be represented
  47  // by your application as a string or numerical representation.
  48  //
  49  // Errors that occur when marshaling will stop the marshaler, and return the error.
  50  //
  51  // Marshal cannot represent cyclic data structures and will not handle them.
  52  // Passing cyclic structures to Marshal will result in an infinite recursion.
  53  type Marshaler interface {
  54  	MarshalSmithyDocument() ([]byte, error)
  55  }
  56  
  57  // Unmarshaler is an interface for a type that unmarshals a document from its protocol-specific representation, and
  58  // stores the result into the value pointed by v. If v is nil or not a pointer then InvalidUnmarshalError will be
  59  // returned.
  60  //
  61  // Unmarshaler supports the same encodings produced by a document Marshaler. This includes support for the `document`
  62  // struct field tag for controlling how struct fields are unmarshaled.
  63  //
  64  // Both generic interface{} and concrete types are valid unmarshal destination types. When unmarshaling a document
  65  // into an empty interface the Unmarshaler will store one of these values:
  66  //   bool,                   for boolean values
  67  //   document.Number,        for arbitrary-precision numbers (int64, float64, big.Int, big.Float)
  68  //   string,                 for string values
  69  //   []interface{},          for array values
  70  //   map[string]interface{}, for objects
  71  //   nil,                    for null values
  72  //
  73  // When unmarshaling, any error that occurs will halt the unmarshal and return the error.
  74  type Unmarshaler interface {
  75  	UnmarshalSmithyDocument(v interface{}) error
  76  }
  77  
  78  type noSerde interface {
  79  	noSmithyDocumentSerde()
  80  }
  81  
  82  // NoSerde is a sentinel value to indicate that a given type should not be marshaled or unmarshaled
  83  // into a protocol document.
  84  type NoSerde struct{}
  85  
  86  func (n NoSerde) noSmithyDocumentSerde() {}
  87  
  88  var _ noSerde = (*NoSerde)(nil)
  89  
  90  // IsNoSerde returns whether the given type implements the no smithy document serde interface.
  91  func IsNoSerde(x interface{}) bool {
  92  	_, ok := x.(noSerde)
  93  	return ok
  94  }
  95  
  96  // Number is an arbitrary precision numerical value
  97  type Number string
  98  
  99  // Int64 returns the number as a string.
 100  func (n Number) String() string {
 101  	return string(n)
 102  }
 103  
 104  // Int64 returns the number as an int64.
 105  func (n Number) Int64() (int64, error) {
 106  	return n.intOfBitSize(64)
 107  }
 108  
 109  func (n Number) intOfBitSize(bitSize int) (int64, error) {
 110  	return strconv.ParseInt(string(n), 10, bitSize)
 111  }
 112  
 113  // Uint64 returns the number as a uint64.
 114  func (n Number) Uint64() (uint64, error) {
 115  	return n.uintOfBitSize(64)
 116  }
 117  
 118  func (n Number) uintOfBitSize(bitSize int) (uint64, error) {
 119  	return strconv.ParseUint(string(n), 10, bitSize)
 120  }
 121  
 122  // Float32 returns the number parsed as a 32-bit float, returns a float64.
 123  func (n Number) Float32() (float64, error) {
 124  	return n.floatOfBitSize(32)
 125  }
 126  
 127  // Float64 returns the number as a float64.
 128  func (n Number) Float64() (float64, error) {
 129  	return n.floatOfBitSize(64)
 130  }
 131  
 132  // Float64 returns the number as a float64.
 133  func (n Number) floatOfBitSize(bitSize int) (float64, error) {
 134  	return strconv.ParseFloat(string(n), bitSize)
 135  }
 136  
 137  // BigFloat attempts to convert the number to a big.Float, returns an error if the operation fails.
 138  func (n Number) BigFloat() (*big.Float, error) {
 139  	f, ok := (&big.Float{}).SetString(string(n))
 140  	if !ok {
 141  		return nil, fmt.Errorf("failed to convert to big.Float")
 142  	}
 143  	return f, nil
 144  }
 145  
 146  // BigInt attempts to convert the number to a big.Int, returns an error if the operation fails.
 147  func (n Number) BigInt() (*big.Int, error) {
 148  	f, ok := (&big.Int{}).SetString(string(n), 10)
 149  	if !ok {
 150  		return nil, fmt.Errorf("failed to convert to big.Float")
 151  	}
 152  	return f, nil
 153  }
 154