295 lines
6.6 KiB
Go
295 lines
6.6 KiB
Go
package eventstream
|
|
|
|
import (
|
|
"fmt"
|
|
"math/big"
|
|
"time"
|
|
|
|
"github.com/aws/smithy-go"
|
|
"github.com/aws/smithy-go/document"
|
|
"github.com/aws/smithy-go/traits"
|
|
)
|
|
|
|
// ShapeDeserializer wraps a [smithy.ShapeDeserializer] to handle event stream
|
|
// message binding traits.
|
|
type ShapeDeserializer struct {
|
|
Message *Message
|
|
|
|
inner smithy.ShapeDeserializer
|
|
|
|
depth int
|
|
schema *smithy.Schema
|
|
|
|
bindings []*smithy.Schema
|
|
bindIdx int
|
|
inBindings bool
|
|
|
|
inBody bool
|
|
hasPayload bool
|
|
hasBody bool
|
|
}
|
|
|
|
var _ smithy.ShapeDeserializer = (*ShapeDeserializer)(nil)
|
|
|
|
// NewShapeDeserializer returns a deserializer for a Message.
|
|
func NewShapeDeserializer(msg *Message, inner smithy.ShapeDeserializer) *ShapeDeserializer {
|
|
return &ShapeDeserializer{
|
|
Message: msg,
|
|
inner: inner,
|
|
}
|
|
}
|
|
|
|
func (d *ShapeDeserializer) ReadStruct(s *smithy.Schema) error {
|
|
d.depth++
|
|
if d.depth > 1 {
|
|
return d.inner.ReadStruct(s)
|
|
}
|
|
d.schema = s
|
|
for _, m := range s.Members() {
|
|
if _, ok := smithy.SchemaTrait[*traits.EventPayload](m); ok {
|
|
d.hasPayload = true
|
|
}
|
|
if isEventBound(m) {
|
|
d.bindings = append(d.bindings, m)
|
|
} else {
|
|
d.hasBody = true
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (d *ShapeDeserializer) ReadStructMember() (*smithy.Schema, error) {
|
|
if d.depth > 1 {
|
|
ms, err := d.inner.ReadStructMember()
|
|
if ms == nil {
|
|
d.depth--
|
|
}
|
|
return ms, err
|
|
}
|
|
|
|
// like httpbinding, throw back the bound stuff first before we drop into
|
|
// the body
|
|
for d.bindIdx < len(d.bindings) {
|
|
m := d.bindings[d.bindIdx]
|
|
d.bindIdx++
|
|
if isEventHeader(m) && d.Message.Headers.Get(m.MemberName()) == nil {
|
|
continue
|
|
}
|
|
d.inBindings = true
|
|
return m, nil
|
|
}
|
|
d.inBindings = false
|
|
|
|
if d.hasPayload {
|
|
d.depth--
|
|
return nil, nil
|
|
}
|
|
|
|
if !d.hasBody {
|
|
d.depth--
|
|
return nil, nil
|
|
}
|
|
|
|
if !d.inBody {
|
|
d.inBody = true
|
|
if err := d.inner.ReadStruct(d.schema); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
ms, err := d.inner.ReadStructMember()
|
|
if ms == nil {
|
|
d.depth--
|
|
}
|
|
|
|
return ms, err
|
|
}
|
|
|
|
func (d *ShapeDeserializer) ReadString(s *smithy.Schema, v *string) error {
|
|
if d.inBindings {
|
|
if isEventHeader(s) {
|
|
hv := d.Message.Headers.Get(s.MemberName())
|
|
if hv == nil {
|
|
return nil
|
|
}
|
|
sv, ok := hv.(StringValue)
|
|
if !ok {
|
|
return fmt.Errorf("event header %q: expected string, got %T", s.MemberName(), hv)
|
|
}
|
|
*v = string(sv)
|
|
return nil
|
|
}
|
|
if isEventPayload(s) {
|
|
*v = string(d.Message.Payload)
|
|
return nil
|
|
}
|
|
}
|
|
return d.inner.ReadString(s, v)
|
|
}
|
|
|
|
func (d *ShapeDeserializer) ReadBool(s *smithy.Schema, v *bool) error {
|
|
if d.inBindings && isEventHeader(s) {
|
|
hv := d.Message.Headers.Get(s.MemberName())
|
|
if hv == nil {
|
|
return nil
|
|
}
|
|
bv, ok := hv.(BoolValue)
|
|
if !ok {
|
|
return fmt.Errorf("event header %q: expected bool, got %T", s.MemberName(), hv)
|
|
}
|
|
*v = bool(bv)
|
|
return nil
|
|
}
|
|
return d.inner.ReadBool(s, v)
|
|
}
|
|
|
|
func (d *ShapeDeserializer) readHeaderInt64(name string) (int64, bool, error) {
|
|
hv := d.Message.Headers.Get(name)
|
|
if hv == nil {
|
|
return 0, false, nil
|
|
}
|
|
switch v := hv.(type) {
|
|
case Int8Value:
|
|
return int64(v), true, nil
|
|
case Int16Value:
|
|
return int64(v), true, nil
|
|
case Int32Value:
|
|
return int64(v), true, nil
|
|
case Int64Value:
|
|
return int64(v), true, nil
|
|
default:
|
|
return 0, false, fmt.Errorf("event header %q: expected integer, got %T", name, hv)
|
|
}
|
|
}
|
|
|
|
type intn interface {
|
|
int8 | int16 | int32 | int64
|
|
}
|
|
|
|
func readEventHeaderInt[T intn](d *ShapeDeserializer, s *smithy.Schema, v *T) error {
|
|
n, ok, err := d.readHeaderInt64(s.MemberName())
|
|
if err != nil || !ok {
|
|
return err
|
|
}
|
|
*v = T(n)
|
|
return nil
|
|
}
|
|
|
|
func (d *ShapeDeserializer) ReadInt8(s *smithy.Schema, v *int8) error {
|
|
if d.inBindings && isEventHeader(s) {
|
|
return readEventHeaderInt(d, s, v)
|
|
}
|
|
return d.inner.ReadInt8(s, v)
|
|
}
|
|
|
|
func (d *ShapeDeserializer) ReadInt16(s *smithy.Schema, v *int16) error {
|
|
if d.inBindings && isEventHeader(s) {
|
|
return readEventHeaderInt(d, s, v)
|
|
}
|
|
return d.inner.ReadInt16(s, v)
|
|
}
|
|
|
|
func (d *ShapeDeserializer) ReadInt32(s *smithy.Schema, v *int32) error {
|
|
if d.inBindings && isEventHeader(s) {
|
|
return readEventHeaderInt(d, s, v)
|
|
}
|
|
return d.inner.ReadInt32(s, v)
|
|
}
|
|
|
|
func (d *ShapeDeserializer) ReadInt64(s *smithy.Schema, v *int64) error {
|
|
if d.inBindings && isEventHeader(s) {
|
|
return readEventHeaderInt(d, s, v)
|
|
}
|
|
return d.inner.ReadInt64(s, v)
|
|
}
|
|
|
|
func (d *ShapeDeserializer) ReadFloat32(s *smithy.Schema, v *float32) error {
|
|
return d.inner.ReadFloat32(s, v)
|
|
}
|
|
|
|
func (d *ShapeDeserializer) ReadFloat64(s *smithy.Schema, v *float64) error {
|
|
return d.inner.ReadFloat64(s, v)
|
|
}
|
|
|
|
func (d *ShapeDeserializer) ReadBlob(s *smithy.Schema, v *[]byte) error {
|
|
if d.inBindings {
|
|
if isEventHeader(s) {
|
|
hv := d.Message.Headers.Get(s.MemberName())
|
|
if hv == nil {
|
|
return nil
|
|
}
|
|
bv, ok := hv.(BytesValue)
|
|
if !ok {
|
|
return fmt.Errorf("event header %q: expected bytes, got %T", s.MemberName(), hv)
|
|
}
|
|
*v = []byte(bv)
|
|
return nil
|
|
}
|
|
if isEventPayload(s) {
|
|
*v = d.Message.Payload
|
|
return nil
|
|
}
|
|
}
|
|
return d.inner.ReadBlob(s, v)
|
|
}
|
|
|
|
func (d *ShapeDeserializer) ReadTime(s *smithy.Schema, v *time.Time) error {
|
|
if d.inBindings && isEventHeader(s) {
|
|
hv := d.Message.Headers.Get(s.MemberName())
|
|
if hv == nil {
|
|
return nil
|
|
}
|
|
tv, ok := hv.(TimestampValue)
|
|
if !ok {
|
|
return fmt.Errorf("event header %q: expected timestamp, got %T", s.MemberName(), hv)
|
|
}
|
|
*v = time.Time(tv)
|
|
return nil
|
|
}
|
|
return d.inner.ReadTime(s, v)
|
|
}
|
|
|
|
func (d *ShapeDeserializer) ReadList(s *smithy.Schema) error {
|
|
return d.inner.ReadList(s)
|
|
}
|
|
|
|
func (d *ShapeDeserializer) ReadListItem(s *smithy.Schema) (bool, error) {
|
|
return d.inner.ReadListItem(s)
|
|
}
|
|
|
|
func (d *ShapeDeserializer) ReadMap(s *smithy.Schema) error {
|
|
return d.inner.ReadMap(s)
|
|
}
|
|
|
|
func (d *ShapeDeserializer) ReadMapKey(s *smithy.Schema) (string, bool, error) {
|
|
return d.inner.ReadMapKey(s)
|
|
}
|
|
|
|
func (d *ShapeDeserializer) ReadUnion(s *smithy.Schema) (*smithy.Schema, error) {
|
|
return d.inner.ReadUnion(s)
|
|
}
|
|
|
|
func (d *ShapeDeserializer) ReadNil(s *smithy.Schema) (bool, error) {
|
|
return d.inner.ReadNil(s)
|
|
}
|
|
|
|
func (d *ShapeDeserializer) ReadDocument(s *smithy.Schema, v *document.Value) error {
|
|
return d.inner.ReadDocument(s, v)
|
|
}
|
|
|
|
func isEventBound(schema *smithy.Schema) bool {
|
|
_, h := smithy.SchemaTrait[*traits.EventHeader](schema)
|
|
_, p := smithy.SchemaTrait[*traits.EventPayload](schema)
|
|
return h || p
|
|
}
|
|
|
|
// ReadBigInt is unimplemented and will return an error.
|
|
func (d *ShapeDeserializer) ReadBigInt(_ *smithy.Schema, _ *big.Int) error {
|
|
return fmt.Errorf("unimplemented")
|
|
}
|
|
|
|
// ReadBigFloat is unimplemented and will return an error.
|
|
func (d *ShapeDeserializer) ReadBigFloat(_ *smithy.Schema, _ *big.Float) error {
|
|
return fmt.Errorf("unimplemented")
|
|
}
|