src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

document.go (7423B)


      1 package document
      2 
      3 import (
      4 	"fmt"
      5 	"math/big"
      6 	"strconv"
      7 	"time"
      8 )
      9 
     10 // Marshaler is an interface for a type that marshals a document to its protocol-specific byte representation and
     11 // returns the resulting bytes. A non-nil error will be returned if an error is encountered during marshaling.
     12 //
     13 // Marshal supports basic scalars (int,uint,float,bool,string), big.Int, and big.Float, maps, slices, and structs.
     14 // Anonymous nested types are flattened based on Go anonymous type visibility.
     15 //
     16 // When defining struct types. the `document` struct tag can be used to control how the value will be
     17 // marshaled into the resulting protocol document.
     18 //
     19 //	// Field is ignored
     20 //	Field int `document:"-"`
     21 //
     22 //	// Field object of key "myName"
     23 //	Field int `document:"myName"`
     24 //
     25 //	// Field object key of key "myName", and
     26 //	// Field is omitted if the field is a zero value for the type.
     27 //	Field int `document:"myName,omitempty"`
     28 //
     29 //	// Field object key of "Field", and
     30 //	// Field is omitted if the field is a zero value for the type.
     31 //	Field int `document:",omitempty"`
     32 //
     33 // All struct fields, including anonymous fields, are marshaled unless the
     34 // any of the following conditions are meet.
     35 //
     36 //   - the field is not exported
     37 //   - document field tag is "-"
     38 //   - document field tag specifies "omitempty", and is a zero value.
     39 //
     40 // Pointer and interface values are encoded as the value pointed to or
     41 // contained in the interface. A nil value encodes as a null
     42 // value unless `omitempty` struct tag is provided.
     43 //
     44 // Channel, complex, and function values are not encoded and will be skipped
     45 // when walking the value to be marshaled.
     46 //
     47 // time.Time is not supported and will cause the Marshaler to return an error. These values should be represented
     48 // by your application as a string or numerical representation.
     49 //
     50 // Errors that occur when marshaling will stop the marshaler, and return the error.
     51 //
     52 // Marshal cannot represent cyclic data structures and will not handle them.
     53 // Passing cyclic structures to Marshal will result in an infinite recursion.
     54 //
     55 // Marshaler is not used in schema-serde based services (which are currently
     56 // being rolled out) since having an implementation of Marshaler locks a
     57 // document into support for a specific serial format. Existing implementations
     58 // of Marshaler will continue to encode to JSON as that is effectively the only
     59 // serial format supported for Document prior to the introduction of
     60 // schema-serde. In schema-serde services it is replaced by [Value].
     61 type Marshaler interface {
     62 	MarshalSmithyDocument() ([]byte, error)
     63 }
     64 
     65 // Unmarshaler is an interface for a type that unmarshals a document from its protocol-specific representation, and
     66 // stores the result into the value pointed by v. If v is nil or not a pointer then InvalidUnmarshalError will be
     67 // returned.
     68 //
     69 // Unmarshaler supports the same encodings produced by a document Marshaler. This includes support for the `document`
     70 // struct field tag for controlling how struct fields are unmarshaled.
     71 //
     72 // Both generic interface{} and concrete types are valid unmarshal destination types. When unmarshaling a document
     73 // into an empty interface the Unmarshaler will store one of these values:
     74 //
     75 //	bool,                   for boolean values
     76 //	document.Number,        for arbitrary-precision numbers (int64, float64, big.Int, big.Float)
     77 //	string,                 for string values
     78 //	[]interface{},          for array values
     79 //	map[string]interface{}, for objects
     80 //	nil,                    for null values
     81 //
     82 // When unmarshaling, any error that occurs will halt the unmarshal and return the error.
     83 type Unmarshaler interface {
     84 	UnmarshalSmithyDocument(v interface{}) error
     85 }
     86 
     87 // Value is a sealed type representing a Smithy document value. It covers the
     88 // full Smithy data model including blob and timestamp.
     89 //
     90 // The following types implement Value:
     91 //   - [Null]
     92 //   - [Boolean]
     93 //   - [Number]
     94 //   - [String]
     95 //   - [Blob]
     96 //   - [Timestamp]
     97 //   - [List]
     98 //   - [Map]
     99 //   - [Structure]
    100 //   - [Opaque]
    101 type Value interface {
    102 	isValue()
    103 }
    104 
    105 // Null is a document null value.
    106 type Null struct{}
    107 
    108 func (Null) isValue() {}
    109 
    110 // Boolean is a document boolean value.
    111 type Boolean bool
    112 
    113 func (Boolean) isValue() {}
    114 
    115 // String is a document string value.
    116 type String string
    117 
    118 func (String) isValue() {}
    119 
    120 // Blob is a document blob value.
    121 type Blob []byte
    122 
    123 func (Blob) isValue() {}
    124 
    125 // Timestamp is a document timestamp value.
    126 type Timestamp time.Time
    127 
    128 func (Timestamp) isValue() {}
    129 
    130 // List is a document list value.
    131 type List []Value
    132 
    133 func (List) isValue() {}
    134 
    135 // Map is a document map value with string keys.
    136 type Map map[string]Value
    137 
    138 func (Map) isValue() {}
    139 
    140 // Structure is a document structure value with an optional discriminator
    141 // identifying the shape it represents.
    142 type Structure struct {
    143 	// Discriminator is the absolute shape ID (e.g.
    144 	// "com.example#MyShape") of the concrete type this structure
    145 	// represents. It may be empty if the type is unknown.
    146 	Discriminator string
    147 
    148 	// Members maps member names to their document values.
    149 	Members map[string]Value
    150 }
    151 
    152 func (Structure) isValue() {}
    153 
    154 // Opaque wraps an arbitrary Go value for backward compatibility with the
    155 // legacy reflection-based document serialization path.
    156 type Opaque struct {
    157 	Value any
    158 }
    159 
    160 func (Opaque) isValue() {}
    161 
    162 type noSerde interface {
    163 	noSmithyDocumentSerde()
    164 }
    165 
    166 // NoSerde is a sentinel value to indicate that a given type should not be marshaled or unmarshaled
    167 // into a protocol document.
    168 type NoSerde struct{}
    169 
    170 func (n NoSerde) noSmithyDocumentSerde() {}
    171 
    172 var _ noSerde = (*NoSerde)(nil)
    173 
    174 // IsNoSerde returns whether the given type implements the no smithy document serde interface.
    175 func IsNoSerde(x interface{}) bool {
    176 	_, ok := x.(noSerde)
    177 	return ok
    178 }
    179 
    180 // Number is an arbitrary precision numerical value
    181 type Number string
    182 
    183 func (Number) isValue() {}
    184 
    185 // Int64 returns the number as a string.
    186 func (n Number) String() string {
    187 	return string(n)
    188 }
    189 
    190 // Int64 returns the number as an int64.
    191 func (n Number) Int64() (int64, error) {
    192 	return n.intOfBitSize(64)
    193 }
    194 
    195 func (n Number) intOfBitSize(bitSize int) (int64, error) {
    196 	return strconv.ParseInt(string(n), 10, bitSize)
    197 }
    198 
    199 // Uint64 returns the number as a uint64.
    200 func (n Number) Uint64() (uint64, error) {
    201 	return n.uintOfBitSize(64)
    202 }
    203 
    204 func (n Number) uintOfBitSize(bitSize int) (uint64, error) {
    205 	return strconv.ParseUint(string(n), 10, bitSize)
    206 }
    207 
    208 // Float32 returns the number parsed as a 32-bit float, returns a float64.
    209 func (n Number) Float32() (float64, error) {
    210 	return n.floatOfBitSize(32)
    211 }
    212 
    213 // Float64 returns the number as a float64.
    214 func (n Number) Float64() (float64, error) {
    215 	return n.floatOfBitSize(64)
    216 }
    217 
    218 // Float64 returns the number as a float64.
    219 func (n Number) floatOfBitSize(bitSize int) (float64, error) {
    220 	return strconv.ParseFloat(string(n), bitSize)
    221 }
    222 
    223 // BigFloat attempts to convert the number to a big.Float, returns an error if the operation fails.
    224 func (n Number) BigFloat() (*big.Float, error) {
    225 	f, ok := (&big.Float{}).SetString(string(n))
    226 	if !ok {
    227 		return nil, fmt.Errorf("failed to convert to big.Float")
    228 	}
    229 	return f, nil
    230 }
    231 
    232 // BigInt attempts to convert the number to a big.Int, returns an error if the operation fails.
    233 func (n Number) BigInt() (*big.Int, error) {
    234 	f, ok := (&big.Int{}).SetString(string(n), 10)
    235 	if !ok {
    236 		return nil, fmt.Errorf("failed to convert to big.Float")
    237 	}
    238 	return f, nil
    239 }