Vendor dependencies
This commit is contained in:
+332
@@ -0,0 +1,332 @@
|
||||
package smithy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// ShapeType is a type of Smithy shape.
|
||||
// See https://smithy.io/2.0/spec/idl.html#defining-shapes.
|
||||
type ShapeType int
|
||||
|
||||
// Enumerates ShapeType per the Smithy IDL.
|
||||
const (
|
||||
ShapeTypeBlob ShapeType = iota
|
||||
ShapeTypeBoolean
|
||||
ShapeTypeString
|
||||
ShapeTypeTimestamp
|
||||
ShapeTypeByte
|
||||
ShapeTypeShort
|
||||
ShapeTypeInteger
|
||||
ShapeTypeLong
|
||||
ShapeTypeFloat
|
||||
ShapeTypeDocument
|
||||
ShapeTypeDouble
|
||||
ShapeTypeBigDecimal
|
||||
ShapeTypeBigInteger
|
||||
ShapeTypeEnum
|
||||
ShapeTypeIntEnum
|
||||
ShapeTypeList
|
||||
ShapeTypeSet
|
||||
ShapeTypeMap
|
||||
ShapeTypeStructure
|
||||
ShapeTypeUnion
|
||||
ShapeTypeMember
|
||||
ShapeTypeService
|
||||
ShapeTypeResource
|
||||
ShapeTypeOperation
|
||||
)
|
||||
|
||||
// ShapeID fields of a Smithy shape ID.
|
||||
type ShapeID struct {
|
||||
Namespace, Name, Member string
|
||||
}
|
||||
|
||||
// String returns the IDL microformat for the shape ID.
|
||||
func (s ShapeID) String() string {
|
||||
if s.Member == "" {
|
||||
return fmt.Sprintf("%s#%s", s.Namespace, s.Name)
|
||||
}
|
||||
return fmt.Sprintf("%s#%s$%s", s.Namespace, s.Name, s.Member)
|
||||
}
|
||||
|
||||
func stoid(s string) ShapeID {
|
||||
ns, n, _ := strings.Cut(s, "#")
|
||||
n, m, _ := strings.Cut(n, "$")
|
||||
return ShapeID{ns, n, m}
|
||||
}
|
||||
|
||||
// Schema encodes information about a shape from a Smithy model.
|
||||
//
|
||||
// Generated clients use schemas at runtime to dynamically (de)serialize
|
||||
// request/responses.
|
||||
type Schema struct {
|
||||
id ShapeID
|
||||
typ ShapeType
|
||||
members map[string]*Schema // member name -> schema
|
||||
traits map[ShapeID]Trait // trait ID -> non-indexed traits only
|
||||
indexed []Trait // indexed trait slots, sized to max index present
|
||||
directMask uint64 // bitmask: bit i set means indexed[i] was declared directly on this schema
|
||||
targetID ShapeID // for member schemas, the target's shape ID
|
||||
|
||||
// resolved on the fly and cached
|
||||
listMember atomic.Pointer[Schema]
|
||||
mapKey, mapValue atomic.Pointer[Schema]
|
||||
|
||||
ext [numExtensionSlots]unsafe.Pointer // lazily-computed codec extensions, accessed atomically
|
||||
}
|
||||
|
||||
// NewSchema creates a new Schema with the given shape ID and traits.
|
||||
func NewSchema(id ShapeID, typ ShapeType, numMembers int, ts ...Trait) *Schema {
|
||||
s := &Schema{
|
||||
id: id,
|
||||
typ: typ,
|
||||
members: make(map[string]*Schema, numMembers),
|
||||
}
|
||||
for _, t := range ts {
|
||||
s.addTrait(t, true)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Schema) addTrait(t Trait, direct bool) {
|
||||
if it, ok := t.(IndexableTrait); ok {
|
||||
idx := it.TraitIndex()
|
||||
if idx >= len(s.indexed) {
|
||||
s.indexed = append(s.indexed, make([]Trait, idx-len(s.indexed)+1)...)
|
||||
}
|
||||
s.indexed[idx] = t
|
||||
if direct {
|
||||
s.directMask |= 1 << uint(idx)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if s.traits == nil {
|
||||
s.traits = map[ShapeID]Trait{}
|
||||
}
|
||||
s.traits[t.TraitID()] = t
|
||||
}
|
||||
|
||||
// AddMember adds a member to the schema derived from the target, with
|
||||
// optional trait overrides. The member schema is returned for caller
|
||||
// reference.
|
||||
//
|
||||
// The member schema's effective trait view (accessed via [SchemaTrait])
|
||||
// inherits all of the target's traits, then applies the overrides. The
|
||||
// member's direct trait view (accessed via [SchemaDirectTrait]) contains
|
||||
// only the overrides, i.e. the traits declared directly on the member.
|
||||
func (s *Schema) AddMember(name string, target *Schema, ts ...Trait) *Schema {
|
||||
m := &Schema{
|
||||
id: ShapeID{Member: name},
|
||||
typ: target.typ,
|
||||
members: target.members,
|
||||
indexed: cloneIndexed(target.indexed),
|
||||
traits: cloneTraits(target.traits),
|
||||
directMask: 0, // inherited traits are not direct
|
||||
targetID: target.id,
|
||||
}
|
||||
|
||||
// member-declared traits override and are direct
|
||||
for _, t := range ts {
|
||||
m.addTrait(t, true)
|
||||
}
|
||||
|
||||
s.members[name] = m
|
||||
|
||||
// Invalidate cached extensions, schema structure changed.
|
||||
for i := range s.ext {
|
||||
atomic.StorePointer(&s.ext[i], nil)
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
func cloneIndexed(src []Trait) []Trait {
|
||||
if src == nil {
|
||||
return nil
|
||||
}
|
||||
dst := make([]Trait, len(src))
|
||||
copy(dst, src)
|
||||
return dst
|
||||
}
|
||||
|
||||
func cloneTraits(src map[ShapeID]Trait) map[ShapeID]Trait {
|
||||
if src == nil {
|
||||
return nil
|
||||
}
|
||||
dst := make(map[ShapeID]Trait, len(src))
|
||||
for k, v := range src {
|
||||
dst[k] = v
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// ListMember returns the "member" schema for list types.
|
||||
func (s *Schema) ListMember() *Schema {
|
||||
return s.lookup(&s.listMember, "member")
|
||||
}
|
||||
|
||||
// MapKey returns the "key" schema for map types.
|
||||
func (s *Schema) MapKey() *Schema {
|
||||
return s.lookup(&s.mapKey, "key")
|
||||
}
|
||||
|
||||
// MapValue returns the "value" schema for map types.
|
||||
func (s *Schema) MapValue() *Schema {
|
||||
return s.lookup(&s.mapValue, "value")
|
||||
}
|
||||
|
||||
func (s *Schema) lookup(cached *atomic.Pointer[Schema], name string) *Schema {
|
||||
if v := cached.Load(); v != nil {
|
||||
return v
|
||||
}
|
||||
|
||||
m, ok := s.members[name]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
cached.Store(m)
|
||||
return m
|
||||
}
|
||||
|
||||
// MemberName returns the member component of the schema's shape ID.
|
||||
func (s *Schema) MemberName() string {
|
||||
return s.id.Member
|
||||
}
|
||||
|
||||
// ID returns the shape ID of the schema.
|
||||
func (s *Schema) ID() ShapeID {
|
||||
return s.id
|
||||
}
|
||||
|
||||
// TargetID returns the shape ID of the member's target shape.
|
||||
func (s *Schema) TargetID() ShapeID {
|
||||
return s.targetID
|
||||
}
|
||||
|
||||
// Type returns the shape type of the schema.
|
||||
func (s *Schema) Type() ShapeType {
|
||||
return s.typ
|
||||
}
|
||||
|
||||
// Member returns the member schema for the given name, or nil.
|
||||
func (s *Schema) Member(name string) *Schema {
|
||||
return s.members[name]
|
||||
}
|
||||
|
||||
// Members returns the schema's members as a map of name to schema.
|
||||
func (s *Schema) Members() map[string]*Schema {
|
||||
return s.members
|
||||
}
|
||||
|
||||
// OperationSchema describes an operation, which is essentially its own schema
|
||||
// with additional pointers to its input and output.
|
||||
type OperationSchema struct {
|
||||
*Schema
|
||||
Input, Output *Schema
|
||||
|
||||
inputStream, outputStream bool
|
||||
}
|
||||
|
||||
// NewOperationSchema returns an OperationSchema for (input, output).
|
||||
func NewOperationSchema(op, input, output *Schema) *OperationSchema {
|
||||
return &OperationSchema{
|
||||
Schema: op,
|
||||
Input: input,
|
||||
Output: output,
|
||||
inputStream: isEventStream(input),
|
||||
outputStream: isEventStream(output),
|
||||
}
|
||||
}
|
||||
|
||||
// IsInputEventStream reports whether this is an input event stream.
|
||||
func (s *OperationSchema) IsInputEventStream() bool {
|
||||
return s.inputStream
|
||||
}
|
||||
|
||||
// IsOutputEventStream reports whether this is an output event stream.
|
||||
func (s *OperationSchema) IsOutputEventStream() bool {
|
||||
return s.outputStream
|
||||
}
|
||||
|
||||
// ServiceSchema describes a service shape.
|
||||
type ServiceSchema struct {
|
||||
*Schema
|
||||
Version string
|
||||
}
|
||||
|
||||
// NewServiceSchema returns a ServiceSchema for the given service shape.
|
||||
func NewServiceSchema(schema *Schema, version string) *ServiceSchema {
|
||||
return &ServiceSchema{Schema: schema, Version: version}
|
||||
}
|
||||
|
||||
// SchemaTrait returns the target trait on the schema if it exists.
|
||||
//
|
||||
// For member schemas this returns the effective trait, which is the trait
|
||||
// declared directly on the member if present, else the trait inherited from
|
||||
// the target shape.
|
||||
func SchemaTrait[T Trait](s *Schema) (T, bool) {
|
||||
return schemaTrait[T](s, false)
|
||||
}
|
||||
|
||||
// SchemaDirectTrait returns the target trait on the schema if it was
|
||||
// declared directly on the schema.
|
||||
//
|
||||
// For member schemas this returns the trait only if it was declared on the
|
||||
// member itself, ignoring any trait inherited from the target shape. For
|
||||
// non-member schemas this is equivalent to [SchemaTrait].
|
||||
func SchemaDirectTrait[T Trait](s *Schema) (T, bool) {
|
||||
return schemaTrait[T](s, true)
|
||||
}
|
||||
|
||||
func schemaTrait[T Trait](s *Schema, directOnly bool) (T, bool) {
|
||||
var zero T
|
||||
|
||||
if s == nil {
|
||||
return zero, false
|
||||
}
|
||||
|
||||
if it, ok := Trait(zero).(IndexableTrait); ok {
|
||||
idx := it.TraitIndex()
|
||||
if idx >= len(s.indexed) {
|
||||
return zero, false
|
||||
}
|
||||
if directOnly && s.directMask&(1<<uint(idx)) == 0 {
|
||||
return zero, false
|
||||
}
|
||||
tt, ok := s.indexed[idx].(T)
|
||||
return tt, ok
|
||||
}
|
||||
|
||||
opaque, ok := s.traits[zero.TraitID()]
|
||||
if !ok {
|
||||
return zero, false
|
||||
}
|
||||
|
||||
tt, ok := opaque.(T)
|
||||
return tt, ok
|
||||
}
|
||||
|
||||
// indexStreaming is the indexed trait slot for @streaming, mirrored from
|
||||
// traits.indexStreaming. We can't import the traits package from here due to a
|
||||
// circular dependency.
|
||||
const indexStreaming = 17
|
||||
|
||||
func isEventStream(s *Schema) bool {
|
||||
if s == nil {
|
||||
return false
|
||||
}
|
||||
for _, m := range s.members {
|
||||
if m.typ != ShapeTypeUnion {
|
||||
continue
|
||||
}
|
||||
if len(m.indexed) > indexStreaming && m.indexed[indexStreaming] != nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user