...

Source file src/solace.dev/go/messaging/pkg/solace/message/sdt/structured_data_types.go

Documentation: solace.dev/go/messaging/pkg/solace/message/sdt

     1  // pubsubplus-go-client
     2  //
     3  // Copyright 2021-2024 Solace Corporation. All rights reserved.
     4  //
     5  // Licensed under the Apache License, Version 2.0 (the "License");
     6  // you may not use this file except in compliance with the License.
     7  // You may obtain a copy of the License at
     8  //
     9  // http://www.apache.org/licenses/LICENSE-2.0
    10  //
    11  // Unless required by applicable law or agreed to in writing, software
    12  // distributed under the License is distributed on an "AS IS" BASIS,
    13  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14  // See the License for the specific language governing permissions and
    15  // limitations under the License.
    16  
    17  // Package sdt contains the types needed to work with Structured Data on a message.
    18  // In particular, two main data types are provided, sdt.Map and sdt.Stream. They can both be
    19  // used as normal Golang maps and slices as well as can be passed as a payload on a message.
    20  // When retrieved, a map will contain the converted data converted as per the table on sdt.Data.
    21  // Furthermore, data types can be converted using the various getters such as GetBool which will
    22  // attempt to convert the specified data into a builtin.bool.
    23  package sdt
    24  
    25  import (
    26  	"fmt"
    27  	"strconv"
    28  	"unsafe"
    29  
    30  	"solace.dev/go/messaging/pkg/solace/resource"
    31  )
    32  
    33  // Map represents a map between strings and values that can be converted into structured
    34  // data types in a message payload. Accepts any valid sdt.Data types.
    35  type Map map[string]Data
    36  
    37  // Stream represents a stream of data that can be converted into structured data types
    38  // in a message payload. Accepts any valid sdt.Data types.
    39  type Stream []Data
    40  
    41  // Data represents valid types that can be used in sdt.Map and sdt.Stream instances.
    42  // Valid types are: int, uint, nil, bool, uint8, uint16, uint32, uint64, int8, int16, int32,
    43  // int64, string, rune, float32, float64, byte, []byte, sdt.WChar, resource.Destination,
    44  // sdt.Map and sdt.Stream.
    45  //
    46  //	Message type mapping is as follows:
    47  //	solClient_fieldType     Golang Type
    48  //	SOLCLIENT_BOOL          bool
    49  //	SOLCLIENT_UINT8         uint8
    50  //	SOLCLIENT_INT8          int8
    51  //	SOLCLIENT_UINT16        uint16
    52  //	SOLCLIENT_INT16         int16
    53  //	SOLCLIENT_UINT32        uint32
    54  //	SOLCLIENT_INT32         int32
    55  //	SOLCLIENT_UINT64        uint64
    56  //	SOLCLIENT_INT64         int64
    57  //	SOLCLIENT_WCHAR         sdt.WChar
    58  //	SOLCLIENT_STRING        string
    59  //	SOLCLIENT_BYTEARRAY     []byte
    60  //	SOLCLIENT_FLOAT         float32
    61  //	SOLCLIENT_DOUBLE        float64
    62  //	SOLCLIENT_MAP           sdt.Map
    63  //	SOLCLIENT_STREAM        sdt.Stream
    64  //	SOLCLIENT_NULL          nil
    65  //	SOLCLIENT_DESTINATION   solace.Destination (solace.Queue or solace.Topic)
    66  //	SOLCLIENT_UNKNOWN       []byte
    67  //
    68  // On outbound messages, the following additional types are supported with the following mappings:
    69  //
    70  //	byte      SOLCLIENT_INT8
    71  //	int      SOLCLIENT_INT64
    72  //	uint      SOLCLIENT_UINT64
    73  //	rune      SOLCLIENT_INT32
    74  //
    75  // Notes on mappings:
    76  //
    77  // - int is always be converted to int64, even when on 32-bit architectures
    78  //
    79  // - uint is always be converted to uint64, even when on 32-bit architectures
    80  //
    81  // - byte is the same as int8 and is mapped accordingly
    82  //
    83  // In addition to these mappings, client-side conversions may be made to retrieve data
    84  // in the type you want. The following table is used to determine these conversions:
    85  //
    86  //	Type    |  bool  |  byte  |  int  |  uint  |  uint8  |  int8  |  uint16  | int16  |  uint32  |  int32  |  uint64  |  int64  |  []byte  |  float32  |  float64  |  string  |  WChar  |
    87  //	-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    88  //	bool    |   x        x        x       x         x        x         x         x         x          x         x          x          x
    89  //	byte    |   x        x        x       x         x        x         x         x         x          x         x          x          x
    90  //	int     |   x        x        x       x         x        x         x         x         x          x         x          x          x
    91  //	uint    |   x        x        x       x         x        x         x         x         x          x         x          x          x
    92  //	uint8   |   x        x        x       x         x        x         x         x         x          x         x          x          x
    93  //	int8    |   x        x        x       x         x        x         x         x         x          x         x          x          x
    94  //	uint16  |   x        x        x       x         x        x         x         x         x          x         x          x          x
    95  //	int16   |   x        x        x       x         x        x         x         x         x          x         x          x          x
    96  //	uint32  |   x        x        x       x         x        x         x         x         x          x         x          x          x
    97  //	int32   |   x        x        x       x         x        x         x         x         x          x         x          x          x
    98  //	uint64  |   x        x        x       x         x        x         x         x         x          x         x          x          x
    99  //	int64   |   x        x        x       x         x        x         x         x         x          x         x          x          x
   100  //	[]byte  |                                                                                                                         x                                    x
   101  //	float32 |                                                                                                                                     x                        x
   102  //	float64 |                                                                                                                                                    x         x
   103  //	string  |                                                                                                                                                              x        x
   104  //	WChar   |                                       x        x                                                                                                             x        x
   105  //
   106  // For each integer-type conversion, the value will be converted if it is
   107  // within the range of the requested type, otherwise a conversion error
   108  // will be thrown indicating that the value is outside the domain.
   109  // String types will be converted to integers using strconv.Stoi and
   110  // then returned if it is within the range of the requested type.
   111  // For boolean conversions from integer types, all non-zero values map to true.
   112  // For boolean conversion from strings, "true" and "1" map to true, "false" and
   113  // "0" map to false, and all other strings are not converted. WChar conversion
   114  // converts strings of length 1 into a WChar. All other strings are rejected.
   115  //
   116  // In addition, the following one-to-one conversions are used:
   117  //
   118  //	sdt.Map, sdt.Stream
   119  //
   120  // Lastly, Destination is mapped with these getters:
   121  //
   122  //	GetQueue, GetTopic, and GetDestination
   123  type Data interface{}
   124  
   125  // WChar represents the wide character type. WChar is a uint16 value
   126  // holding UTF-16 data. On publish, this value is converted into
   127  // SOLCLIENT_WCHAR, and on receive SOLCLIENT_WCHAR is converted into
   128  // WChar.
   129  type WChar uint16
   130  
   131  // KeyNotFoundError is returned if the requested key is not found.
   132  type KeyNotFoundError struct {
   133  	// Key is the key that was being accessed and does not exist.
   134  	Key string
   135  }
   136  
   137  func (err *KeyNotFoundError) Error() string {
   138  	return fmt.Sprintf("could not find mapping for key %s", err.Key)
   139  }
   140  
   141  // OutOfBoundsError is returned if the specified index is out of bounds.
   142  type OutOfBoundsError struct {
   143  	Index int
   144  }
   145  
   146  func (err *OutOfBoundsError) Error() string {
   147  	return fmt.Sprintf("index %d is out of bounds", err.Index)
   148  }
   149  
   150  // FormatConversionError is returned if there is an error converting
   151  // stored data to a specified return type.
   152  type FormatConversionError struct {
   153  	// Message is the message of the error
   154  	Message string
   155  	// Data is the data that could not be converted.
   156  	Data interface{}
   157  }
   158  
   159  // Error implements the error interface and  returns the error message as string.
   160  func (err *FormatConversionError) Error() string {
   161  	return err.Message
   162  }
   163  
   164  func defaultFormatConversionError(data interface{}, desiredType string) *FormatConversionError {
   165  	return &FormatConversionError{
   166  		Message: fmt.Sprintf("cannot convert %T to %s", data, desiredType),
   167  		Data:    data,
   168  	}
   169  }
   170  
   171  func nestedFormatConversionError(data interface{}, desiredType string, nested error) *FormatConversionError {
   172  	return &FormatConversionError{
   173  		Message: fmt.Sprintf("cannot convert %T to %s: %s", data, desiredType, nested.Error()),
   174  		Data:    data,
   175  	}
   176  }
   177  
   178  // IllegalTypeError is returned if an error occurs while encoding a stored
   179  // type to a message when setting a payload or user property.
   180  type IllegalTypeError struct {
   181  	Data interface{}
   182  }
   183  
   184  // Error implements the error interface and  returns the error message as string.
   185  func (err *IllegalTypeError) Error() string {
   186  	return fmt.Sprintf("type %T is not a valid instance of sdt.Data", err.Data)
   187  }
   188  
   189  // GetBool retrieves the data using the specified key and attempts to convert the
   190  // data into a boolean form, if necessary. Returns the boolean value of the
   191  // field, and one of the following errors if it occurred:
   192  //
   193  // - sdt.KeyNotFoundError - If a mapping for the specified key was not found.
   194  //
   195  // - sdt.FormatConversionError - If the data cannot be converted into boolean form.
   196  func (sdtMap Map) GetBool(key string) (val bool, err error) {
   197  	elem, ok := sdtMap[key]
   198  	if !ok {
   199  		return false, &KeyNotFoundError{key}
   200  	}
   201  	return getBool(elem)
   202  }
   203  
   204  // GetByte retrieves the data at the specified key and attempt to convert the
   205  // data into byte form if necessary. Returns the byte value of the field,
   206  // and one of the following errors if it occurred:
   207  //
   208  // - sdt.KeyNotFoundError - If a mapping for the specified key was not found.
   209  //
   210  // - sdt.FormatConversionError - If the data cannot be converted into byte form.
   211  func (sdtMap Map) GetByte(key string) (val byte, err error) {
   212  	elem, ok := sdtMap[key]
   213  	if !ok {
   214  		return 0, &KeyNotFoundError{key}
   215  	}
   216  	return getUint8(elem)
   217  }
   218  
   219  // GetInt retrieves the data at the specified key and attempt to convert the
   220  // data into int form if necessary. Returns the int value of the
   221  // field, and one of the following errors if it occurred:
   222  //
   223  // - sdt.KeyNotFoundError - If a mapping for the specified key was not found.
   224  //
   225  // - sdt.FormatConversionError - If the data cannot be converted into int form.
   226  func (sdtMap Map) GetInt(key string) (val int, err error) {
   227  	elem, ok := sdtMap[key]
   228  	if !ok {
   229  		return 0, &KeyNotFoundError{key}
   230  	}
   231  	return getInt(elem)
   232  }
   233  
   234  // GetUInt retrieves the data at the specified key and attempt to convert the
   235  // data into uint form if necessary. Returns the uint value of the
   236  // field, and one of the following errors if it occurred:
   237  //
   238  // - sdt.KeyNotFoundError - If a mapping for the specified key was not found.
   239  //
   240  // - sdt.FormatConversionError - If the data cannot be converted into uint form.
   241  func (sdtMap Map) GetUInt(key string) (val uint, err error) {
   242  	elem, ok := sdtMap[key]
   243  	if !ok {
   244  		return 0, &KeyNotFoundError{key}
   245  	}
   246  	return getUint(elem)
   247  }
   248  
   249  // GetInt8 retrieves the data at the specified key and attempt to convert the
   250  // data into int8 form if necessary. Returns the int8 value of the
   251  // field, and one of the following errors if it occurred:
   252  //
   253  // - sdt.KeyNotFoundError - If a mapping for the specified key was not found.
   254  //
   255  // - sdt.FormatConversionError - If the data cannot be converted into int8 form.
   256  func (sdtMap Map) GetInt8(key string) (val int8, err error) {
   257  	elem, ok := sdtMap[key]
   258  	if !ok {
   259  		return 0, &KeyNotFoundError{key}
   260  	}
   261  	return getInt8(elem)
   262  }
   263  
   264  // GetInt16 retrieves the data at the specified key and attempt to convert the
   265  // data into int16 form if necessary. Returns the int16 value of the
   266  // field, and one of the following errors if it occurred:
   267  //
   268  // - sdt.KeyNotFoundError - If a mapping for the specified key was not found.
   269  //
   270  // - sdt.FormatConversionError - If the data cannot be converted into int16 form.
   271  func (sdtMap Map) GetInt16(key string) (val int16, err error) {
   272  	elem, ok := sdtMap[key]
   273  	if !ok {
   274  		return 0, &KeyNotFoundError{key}
   275  	}
   276  	return getInt16(elem)
   277  }
   278  
   279  // GetInt32 retrieves the data at the specified key and attempt to convert the
   280  // data into int32 form if necessary. Returns the int32 value of the
   281  // field, and one of the following errors if it occurred:
   282  //
   283  // - sdt.KeyNotFoundError - If a mapping for the specified key was not found.
   284  //
   285  // - sdt.FormatConversionError - If the data cannot be converted into int32 form.
   286  func (sdtMap Map) GetInt32(key string) (val int32, err error) {
   287  	elem, ok := sdtMap[key]
   288  	if !ok {
   289  		return 0, &KeyNotFoundError{key}
   290  	}
   291  	return getInt32(elem)
   292  }
   293  
   294  // GetInt64 retrieves the data at the specified key and attempt to convert the
   295  // data into int64 form if necessary. Returns the int64 value of the
   296  // field, and one of the following errors if it occurred:
   297  //
   298  // - sdt.KeyNotFoundError - If a mapping for the specified key was not found.
   299  //
   300  // - sdt.FormatConversionError - If the data cannot be converted into int64 form.
   301  func (sdtMap Map) GetInt64(key string) (val int64, err error) {
   302  	elem, ok := sdtMap[key]
   303  	if !ok {
   304  		return 0, &KeyNotFoundError{key}
   305  	}
   306  	return getInt64(elem)
   307  }
   308  
   309  // GetUInt8 retrieves the data at the specified key and attempt to convert the
   310  // data into uint8 form if necessary. Returns the uint8 value of the
   311  // field, and one of the following errors if it occurred:
   312  //
   313  // - sdt.KeyNotFoundError - If a mapping for the specified key was not found.
   314  //
   315  // - sdt.FormatConversionError - If the data cannot be converted into uint8 form.
   316  func (sdtMap Map) GetUInt8(key string) (val uint8, err error) {
   317  	elem, ok := sdtMap[key]
   318  	if !ok {
   319  		return 0, &KeyNotFoundError{key}
   320  	}
   321  	return getUint8(elem)
   322  }
   323  
   324  // GetUInt16 retrieves the data at the specified key and attempt to convert the
   325  // data into uint16 form if necessary. Returns the uint16 value of the
   326  // field, and one of the following errors if it occurred:
   327  //
   328  // - sdt.KeyNotFoundError if a mapping for the specified key was not found.
   329  //
   330  // - sdt.FormatConversionError - If the data cannot be converted into uint16 form.
   331  func (sdtMap Map) GetUInt16(key string) (val uint16, err error) {
   332  	elem, ok := sdtMap[key]
   333  	if !ok {
   334  		return 0, &KeyNotFoundError{key}
   335  	}
   336  	return getUint16(elem)
   337  }
   338  
   339  // GetUInt32 retrieves the data at the specified key and attempt to convert the
   340  // data into uint32 form if necessary. Returns the uint32 value of the
   341  // field, and one of the following errors if it occurred:
   342  //
   343  // - sdt.KeyNotFoundError - If a mapping for the specified key was not found.
   344  //
   345  // - sdt.FormatConversionError if the data cannot be converted into uint32 form.
   346  func (sdtMap Map) GetUInt32(key string) (val uint32, err error) {
   347  	elem, ok := sdtMap[key]
   348  	if !ok {
   349  		return 0, &KeyNotFoundError{key}
   350  	}
   351  	return getUint32(elem)
   352  }
   353  
   354  // GetUInt64 retrieves the data at the specified key and attempt to convert the
   355  // data into uint64 form if necessary. Returns the uint64 value of the
   356  // field, and one of the following errors if it occurred:
   357  //
   358  // - sdt.KeyNotFoundError - If a mapping for the specified key was not found.
   359  //
   360  // - sdt.FormatConversionError - If the data cannot be converted into uint64 form
   361  func (sdtMap Map) GetUInt64(key string) (val uint64, err error) {
   362  	elem, ok := sdtMap[key]
   363  	if !ok {
   364  		return 0, &KeyNotFoundError{key}
   365  	}
   366  	return getUint64(elem)
   367  }
   368  
   369  // GetFloat32 retrieves the data at the specified key and attempt to convert the
   370  // data into float32 form if necessary. Returns the float32 value of the
   371  // field, and one of the following errors if it occurred:
   372  //
   373  // - sdt.KeyNotFoundError - If a mapping for the specified key was not found.
   374  //
   375  // - sdt.FormatConversionError - If the data cannot be converted into float32 form.
   376  func (sdtMap Map) GetFloat32(key string) (val float32, err error) {
   377  	elem, ok := sdtMap[key]
   378  	if !ok {
   379  		return 0, &KeyNotFoundError{key}
   380  	}
   381  	return getFloat32(elem)
   382  }
   383  
   384  // GetFloat64 retrieves the data at the specified key and attempt to convert the
   385  // data into float64 form if necessary. Returns the float64 value of the
   386  // field, and one of the following errors if it occurred:
   387  //
   388  // - sdt.KeyNotFoundError - If a mapping for the specified key was not found.
   389  //
   390  // - sdt.FormatConversionError - If the data cannot be converted into float64 form.
   391  func (sdtMap Map) GetFloat64(key string) (val float64, err error) {
   392  	elem, ok := sdtMap[key]
   393  	if !ok {
   394  		return 0, &KeyNotFoundError{key}
   395  	}
   396  	return getFloat64(elem)
   397  }
   398  
   399  // GetString retrieves the data at the specified key and attempt to convert the
   400  // data into string form if necessary. Returns the string value of the
   401  // field, and one of the following errors if it occurred:
   402  //
   403  // - sdt.KeyNotFoundError - If a mapping for the specified key was not found.
   404  //
   405  // - sdt.FormatConversionError - If the data cannot be converted into string form.
   406  func (sdtMap Map) GetString(key string) (val string, err error) {
   407  	elem, ok := sdtMap[key]
   408  	if !ok {
   409  		return "", &KeyNotFoundError{key}
   410  	}
   411  	return getString(elem)
   412  }
   413  
   414  // GetWChar retrieves the data at the specified key and attempt to convert the
   415  // data into WChar form if necessary. Returns the WChar value of the
   416  // field, and one of the following errors if it occurred:
   417  //
   418  // - sdt.KeyNotFoundError - If a mapping for the specified key was not found.
   419  //
   420  // - sdt.FormatConversionError - If the data cannot be converted into WChar form.
   421  func (sdtMap Map) GetWChar(key string) (val WChar, err error) {
   422  	elem, ok := sdtMap[key]
   423  	if !ok {
   424  		return WChar(0), &KeyNotFoundError{key}
   425  	}
   426  	return getWChar(elem)
   427  }
   428  
   429  // GetByteArray retrieves the data at the specified key and attempt to convert the
   430  // data into []byte form if necessary. Returns the []byte value of the
   431  // field, and one of the following errors if it occurred:
   432  //
   433  // - sdt.KeyNotFoundError - If a mapping for the specified key was not found.
   434  //
   435  // - sdt.FormatConversionError - If the data cannot be converted into []byte form.
   436  func (sdtMap Map) GetByteArray(key string) (val []byte, err error) {
   437  	elem, ok := sdtMap[key]
   438  	if !ok {
   439  		return nil, &KeyNotFoundError{key}
   440  	}
   441  	return getByteArray(elem)
   442  }
   443  
   444  // GetMap retrieves the data at the specified key as an sdt.Map.
   445  // One of the following errors may occur:
   446  //
   447  // - sdt.KeyNotFoundError - If a mapping for the specified key was not found.
   448  //
   449  // - sdt.FormatConversionError - If the data is not of type sdt.Map.
   450  func (sdtMap Map) GetMap(key string) (val Map, err error) {
   451  	elem, ok := sdtMap[key]
   452  	if !ok {
   453  		return nil, &KeyNotFoundError{key}
   454  	}
   455  	return getMap(elem)
   456  }
   457  
   458  // GetStream retrieves the data at the specified key as an sdt.Stream. One of the following errors may occur:
   459  //
   460  // - sdt.KeyNotFoundError - If a mapping for the specified key was not found.
   461  //
   462  // - sdt.FormatConversionError - If the data is not of type sdt.Stream.
   463  func (sdtMap Map) GetStream(key string) (val Stream, err error) {
   464  	elem, ok := sdtMap[key]
   465  	if !ok {
   466  		return nil, &KeyNotFoundError{key}
   467  	}
   468  	return getStream(elem)
   469  }
   470  
   471  // GetDestination retrieves the data at the specified key as a Destination.
   472  // One of the following errors may occur:
   473  //
   474  // - sdt.KeyNotFoundError - If a mapping for the specified key was not found.
   475  //
   476  // - sdt.FormatConversionError - If the data is not of type Destination.
   477  func (sdtMap Map) GetDestination(key string) (val resource.Destination, err error) {
   478  	elem, ok := sdtMap[key]
   479  	if !ok {
   480  		return nil, &KeyNotFoundError{key}
   481  	}
   482  	return getDestination(elem)
   483  }
   484  
   485  // GetQueue retrieves the data at the specified key as a Queue.
   486  // One of the following errors may occur:
   487  //
   488  // - sdt.KeyNotFoundError - If a mapping for the specified key was not found.
   489  //
   490  // - sdt.FormatConversionError - If the data is not of type Queue.
   491  func (sdtMap Map) GetQueue(key string) (val *resource.Queue, err error) {
   492  	elem, ok := sdtMap[key]
   493  	if !ok {
   494  		return nil, &KeyNotFoundError{key}
   495  	}
   496  	return getQueue(elem)
   497  }
   498  
   499  // GetTopic retrieves the data at the specified key as a Topic.
   500  // One of the following errors may occur:
   501  //
   502  // - sdt.KeyNotFoundError - If a mapping for the specified key was not found.
   503  //
   504  // - sdt.FormatConversionError - If the data is not of type Topic.
   505  func (sdtMap Map) GetTopic(key string) (val *resource.Topic, err error) {
   506  	elem, ok := sdtMap[key]
   507  	if !ok {
   508  		return nil, &KeyNotFoundError{key}
   509  	}
   510  	return getTopic(elem)
   511  }
   512  
   513  // GetBool retrieves the data at the specified index and attempt to convert the
   514  // data into boolean form if necessary. Returns the boolean value of the
   515  // field, and one of the following errors if it occurred:
   516  //
   517  // - sdt.OutOfBoundsError - If the specified index is out of the stream's bounds.
   518  //
   519  // - sdt.FormatConversionError - If the data cannot be converted into boolean form.
   520  func (sdtStream Stream) GetBool(index int) (val bool, err error) {
   521  	if index >= len(sdtStream) || index < 0 {
   522  		return false, &OutOfBoundsError{Index: index}
   523  	}
   524  	return getBool(sdtStream[index])
   525  }
   526  
   527  // GetByte retrieves the data at the specified index and attempt to convert the
   528  // data into byte form if necessary. Returns the byte value of the field,
   529  // and one of the following errors if it occurred:
   530  //
   531  // - sdt.OutOfBoundsError - If the specified index is out of the stream's bounds.
   532  //
   533  // - sdt.FormatConversionError - If the data cannot be converted into byte form.
   534  func (sdtStream Stream) GetByte(index int) (val byte, err error) {
   535  	if index >= len(sdtStream) || index < 0 {
   536  		return 0, &OutOfBoundsError{Index: index}
   537  	}
   538  	return getUint8(sdtStream[index])
   539  }
   540  
   541  // GetInt retrieves the data at the specified index and attempt to convert the
   542  // data into int form if necessary. Returns the int value of the
   543  // field, and one of the following errors if it occurred:
   544  //
   545  // - sdt.OutOfBoundsError - If the specified index is out of the stream's bounds.
   546  //
   547  // - sdt.FormatConversionError - If the data cannot be converted into int form.
   548  func (sdtStream Stream) GetInt(index int) (val int, err error) {
   549  	if index >= len(sdtStream) || index < 0 {
   550  		return 0, &OutOfBoundsError{Index: index}
   551  	}
   552  	return getInt(sdtStream[index])
   553  }
   554  
   555  // GetUInt retrieves the data at the specified index and attempt to convert the
   556  // data into uint form if necessary. Returns the uint value of the
   557  // field, and one of the following errors if it occurred:
   558  //
   559  // - sdt.OutOfBoundsError - If the specified index is out of the stream's bounds.
   560  //
   561  // - sdt.FormatConversionError - If the data cannot be converted into uint form.
   562  func (sdtStream Stream) GetUInt(index int) (val uint, err error) {
   563  	if index >= len(sdtStream) || index < 0 {
   564  		return 0, &OutOfBoundsError{Index: index}
   565  	}
   566  	return getUint(sdtStream[index])
   567  }
   568  
   569  // GetInt8 retrieves the data at the specified index and attempt to convert the
   570  // data into int8 form if necessary. Returns the int8 value of the
   571  // field, and one of the following errors if it occurred:
   572  //
   573  // - sdt.OutOfBoundsError - If the specified index is out of the stream's bounds.
   574  //
   575  // - sdt.FormatConversionError - If the data cannot be converted into int8 form.
   576  func (sdtStream Stream) GetInt8(index int) (val int8, err error) {
   577  	if index >= len(sdtStream) || index < 0 {
   578  		return 0, &OutOfBoundsError{Index: index}
   579  	}
   580  	return getInt8(sdtStream[index])
   581  }
   582  
   583  // GetInt16 retrieves the data at the specified index and attempt to convert the
   584  // data into int16 form if necessary. Returns the int16 value of the
   585  // field, and one of the following errors if it occurred:
   586  //
   587  // - sdt.OutOfBoundsError - If the specified index is out of the stream's bounds.
   588  //
   589  // - sdt.FormatConversionError - If the data cannot be converted into int16 form.
   590  func (sdtStream Stream) GetInt16(index int) (val int16, err error) {
   591  	if index >= len(sdtStream) || index < 0 {
   592  		return 0, &OutOfBoundsError{Index: index}
   593  	}
   594  	return getInt16(sdtStream[index])
   595  }
   596  
   597  // GetInt32 retrieves the data at the specified index and attempt to convert the
   598  // data into int32 form if necessary. Returns the int32 value of the
   599  // field, and one of the following errors if it occurred:
   600  //
   601  // - sdt.OutOfBoundsError - If the specified index is out of the stream's bounds.
   602  //
   603  // - sdt.FormatConversionError - If the data cannot be converted into int32 form.
   604  func (sdtStream Stream) GetInt32(index int) (val int32, err error) {
   605  	if index >= len(sdtStream) || index < 0 {
   606  		return 0, &OutOfBoundsError{Index: index}
   607  	}
   608  	return getInt32(sdtStream[index])
   609  }
   610  
   611  // GetInt64 retrieves the data at the specified index and attempt to convert the
   612  // data into int64 form if necessary. Returns the int64 value of the
   613  // field, and one of the following errors if it occurred:
   614  //
   615  // - sdt.OutOfBoundsError - If the specified index is out of the stream's bounds.
   616  //
   617  // - sdt.FormatConversionError - If the data cannot be converted into int64 form.
   618  func (sdtStream Stream) GetInt64(index int) (val int64, err error) {
   619  	if index >= len(sdtStream) || index < 0 {
   620  		return 0, &OutOfBoundsError{Index: index}
   621  	}
   622  	return getInt64(sdtStream[index])
   623  }
   624  
   625  // GetUInt8 retrieves the data at the specified index and attempt to convert the
   626  // data into uint8 form if necessary. Returns the uint8 value of the
   627  // field, and one of the following errors if it occurred:
   628  //
   629  // - sdt.OutOfBoundsError - If the specified index is out of the stream's bounds.
   630  //
   631  // - sdt.FormatConversionError - If the data cannot be converted into uint8 form.
   632  func (sdtStream Stream) GetUInt8(index int) (val uint8, err error) {
   633  	if index >= len(sdtStream) || index < 0 {
   634  		return 0, &OutOfBoundsError{Index: index}
   635  	}
   636  	return getUint8(sdtStream[index])
   637  }
   638  
   639  // GetUInt16 retrieves the data at the specified index and attempt to convert the
   640  // data into uint16 form if necessary. Returns the uint16 value of the
   641  // field, and one of the following errors if it occurred:
   642  //
   643  // - sdt.OutOfBoundsError - If the specified index is out of the stream's bounds.
   644  //
   645  // - sdt.FormatConversionError - If the data cannot be converted into uint16 form.
   646  func (sdtStream Stream) GetUInt16(index int) (val uint16, err error) {
   647  	if index >= len(sdtStream) || index < 0 {
   648  		return 0, &OutOfBoundsError{Index: index}
   649  	}
   650  	return getUint16(sdtStream[index])
   651  }
   652  
   653  // GetUInt32 retrieves the data at the specified index and attempt to convert the
   654  // data into uint32 form if necessary. Returns the uint32 value of the
   655  // field, and one of the following errors if it occurred:
   656  //
   657  // - sdt.OutOfBoundsError - If the specified index is out of the stream's bounds.
   658  //
   659  // - sdt.FormatConversionError - If the data cannot be converted into uint32 form.
   660  func (sdtStream Stream) GetUInt32(index int) (val uint32, err error) {
   661  	if index >= len(sdtStream) || index < 0 {
   662  		return 0, &OutOfBoundsError{Index: index}
   663  	}
   664  	return getUint32(sdtStream[index])
   665  }
   666  
   667  // GetUInt64 retrieves the data at the specified index and attempt to convert the
   668  // data into uint64 form if necessary. Returns the uint64 value of the
   669  // field, and one of the following errors if it occurred:
   670  //
   671  // - sdt.OutOfBoundsError - If the specified index is out of the stream's bounds.
   672  //
   673  // - sdt.FormatConversionError - If the data cannot be converted into uint64 form.
   674  func (sdtStream Stream) GetUInt64(index int) (val uint64, err error) {
   675  	if index >= len(sdtStream) || index < 0 {
   676  		return 0, &OutOfBoundsError{Index: index}
   677  	}
   678  	return getUint64(sdtStream[index])
   679  }
   680  
   681  // GetFloat32 retrieves the data at the specified index and attempt to convert the
   682  // data into float32 form if necessary. Returns the float32 value of the
   683  // field, and one of the following errors if it occurred:
   684  //
   685  // - sdt.OutOfBoundsError - If the specified index is out of the stream's bounds.
   686  //
   687  // - sdt.FormatConversionError - If the data cannot be converted into float32 form.
   688  func (sdtStream Stream) GetFloat32(index int) (val float32, err error) {
   689  	if index >= len(sdtStream) || index < 0 {
   690  		return 0, &OutOfBoundsError{Index: index}
   691  	}
   692  	return getFloat32(sdtStream[index])
   693  }
   694  
   695  // GetFloat64 retrieves the data at the specified index and attempt to convert the
   696  // data into float64 form if necessary. Returns the float64 value of the
   697  // field, and one of the following errors if it occurred:
   698  //
   699  // - sdt.OutOfBoundsError - If the specified index is out of the stream's bounds.
   700  //
   701  // - sdt.FormatConversionError - If the data cannot be converted into float64 form.
   702  func (sdtStream Stream) GetFloat64(index int) (val float64, err error) {
   703  	if index >= len(sdtStream) || index < 0 {
   704  		return 0, &OutOfBoundsError{Index: index}
   705  	}
   706  	return getFloat64(sdtStream[index])
   707  }
   708  
   709  // GetString retrieves the data at the specified index and attempt to convert the
   710  // data into string form if necessary. Returns the string value of the
   711  // field, and one of the following errors if it occurred:
   712  //
   713  // - sdt.OutOfBoundsError - If the specified index is out of the stream's bounds.
   714  //
   715  // - sdt.FormatConversionError - If the data cannot be converted into string form.
   716  func (sdtStream Stream) GetString(index int) (val string, err error) {
   717  	if index >= len(sdtStream) || index < 0 {
   718  		return "", &OutOfBoundsError{Index: index}
   719  	}
   720  	return getString(sdtStream[index])
   721  }
   722  
   723  // GetWChar retrieves the data at the specified index and attempt to convert the
   724  // data into WChar form if necessary. Returns the WChar value of the
   725  // field, and one of the following errors if it occurred:
   726  //
   727  // - sdt.OutOfBoundsError - If the specified index is out of the stream's bounds.
   728  //
   729  // - sdt.FormatConversionError - If the data cannot be converted into WChar form.
   730  func (sdtStream Stream) GetWChar(index int) (val WChar, err error) {
   731  	if index >= len(sdtStream) || index < 0 {
   732  		return WChar(0), &OutOfBoundsError{Index: index}
   733  	}
   734  	return getWChar(sdtStream[index])
   735  }
   736  
   737  // GetByteArray retrieves the data at the specified index and attempt to convert the
   738  // data into []byte form if necessary. Returns the []byte value of the
   739  // field, and one of the following errors if it occurred:
   740  //
   741  // - sdt.OutOfBoundsError - If the specified index is out of the stream's bounds.
   742  //
   743  // - sdt.FormatConversionError - If the data cannot be converted into []byte form.
   744  func (sdtStream Stream) GetByteArray(index int) (val []byte, err error) {
   745  	if index >= len(sdtStream) || index < 0 {
   746  		return nil, &OutOfBoundsError{Index: index}
   747  	}
   748  	return getByteArray(sdtStream[index])
   749  }
   750  
   751  // GetMap retrieves the data at the specified index as an sdt.Map.
   752  // One of the following errors may occur:
   753  //
   754  // - sdt.OutOfBoundsError - If the specified index is out of the stream's bounds.
   755  //
   756  // - sdt.FormatConversionError - If the data is not of type sdt.Map.
   757  func (sdtStream Stream) GetMap(index int) (val Map, err error) {
   758  	if index >= len(sdtStream) || index < 0 {
   759  		return nil, &OutOfBoundsError{Index: index}
   760  	}
   761  	return getMap(sdtStream[index])
   762  }
   763  
   764  // GetStream retrieves the data at the specified index as an sdt.Stream.
   765  // One of the following errors may occur:
   766  //
   767  // - sdt.OutOfBoundsError - If the specified index is out of the stream's bounds.
   768  //
   769  // - sdt.FormatConversionError - If the data is not of type sdt.Stream.
   770  func (sdtStream Stream) GetStream(index int) (val Stream, err error) {
   771  	if index >= len(sdtStream) || index < 0 {
   772  		return nil, &OutOfBoundsError{Index: index}
   773  	}
   774  	return getStream(sdtStream[index])
   775  }
   776  
   777  // GetDestination retrieves the data at the specified index as a Destination.
   778  // One of the following errors may occur:
   779  //
   780  // - sdt.OutOfBoundsError - If the specified index is out of the stream's bounds.
   781  //
   782  // - sdt.FormatConversionError - If the data is not of type Destination.
   783  func (sdtStream Stream) GetDestination(index int) (val resource.Destination, err error) {
   784  	if index >= len(sdtStream) || index < 0 {
   785  		return nil, &OutOfBoundsError{Index: index}
   786  	}
   787  	return getDestination(sdtStream[index])
   788  }
   789  
   790  // GetQueue retrieves the data at the specified index as a Queue.
   791  // One of the following errors may occur:
   792  //
   793  // - sdt.OutOfBoundsError - If the specified index is out of the stream's bounds.
   794  //
   795  // - sdt.FormatConversionError - If the data is not of type Queue.
   796  func (sdtStream Stream) GetQueue(index int) (val *resource.Queue, err error) {
   797  	if index >= len(sdtStream) || index < 0 {
   798  		return nil, &OutOfBoundsError{Index: index}
   799  	}
   800  	return getQueue(sdtStream[index])
   801  }
   802  
   803  // GetTopic retrieves the data at the specified index as a Topic.
   804  // One of the following errors may occur:
   805  //
   806  // - sdt.OutOfBoundsError - If the specified index is out of the stream's bounds.
   807  //
   808  // - sdt.FormatConversionError - If the data is not of type Topic.
   809  func (sdtStream Stream) GetTopic(index int) (val *resource.Topic, err error) {
   810  	if index >= len(sdtStream) || index < 0 {
   811  		return nil, &OutOfBoundsError{Index: index}
   812  	}
   813  	return getTopic(sdtStream[index])
   814  }
   815  
   816  func getBool(elem interface{}) (bool, error) {
   817  	switch casted := elem.(type) {
   818  	case bool:
   819  		return casted, nil
   820  	case int:
   821  		return casted != 0, nil
   822  	case uint:
   823  		return casted != 0, nil
   824  	case uint8:
   825  		return casted != 0, nil
   826  	case int8:
   827  		return casted != 0, nil
   828  	case uint16:
   829  		return casted != 0, nil
   830  	case int16:
   831  		return casted != 0, nil
   832  	case uint32:
   833  		return casted != 0, nil
   834  	case int32:
   835  		return casted != 0, nil
   836  	case uint64:
   837  		return casted != 0, nil
   838  	case int64:
   839  		return casted != 0, nil
   840  	case string:
   841  		if casted == "true" || casted == "1" {
   842  			return true, nil
   843  		} else if casted == "false" || casted == "0" {
   844  			return false, nil
   845  		}
   846  		return false, &FormatConversionError{
   847  			fmt.Sprintf("cannot convert string '%s' to bool", casted),
   848  			casted,
   849  		}
   850  	// case []byte:
   851  	// case float32:
   852  	// case float64:
   853  	// case WChar:
   854  	default:
   855  		return false, defaultFormatConversionError(elem, "bool")
   856  	}
   857  }
   858  
   859  func getUint8(elem interface{}) (uint8, error) {
   860  	converted, err := parseUint(elem, 8)
   861  	if err != nil {
   862  		return 0, err
   863  	}
   864  	return uint8(converted), nil
   865  }
   866  
   867  func getInt8(elem interface{}) (int8, error) {
   868  	converted, err := parseInt(elem, 8)
   869  	if err != nil {
   870  		return 0, err
   871  	}
   872  	return int8(converted), nil
   873  }
   874  
   875  func getUint16(elem interface{}) (uint16, error) {
   876  	converted, err := parseUint(elem, 16)
   877  	if err != nil {
   878  		return 0, err
   879  	}
   880  	return uint16(converted), nil
   881  }
   882  
   883  func getInt16(elem interface{}) (int16, error) {
   884  	converted, err := parseInt(elem, 16)
   885  	if err != nil {
   886  		return 0, err
   887  	}
   888  	return int16(converted), nil
   889  }
   890  
   891  func getUint32(elem interface{}) (uint32, error) {
   892  	converted, err := parseUint(elem, 32)
   893  	if err != nil {
   894  		return 0, err
   895  	}
   896  	return uint32(converted), nil
   897  }
   898  
   899  func getInt32(elem interface{}) (int32, error) {
   900  	converted, err := parseInt(elem, 32)
   901  	if err != nil {
   902  		return 0, err
   903  	}
   904  	return int32(converted), nil
   905  }
   906  
   907  func getUint64(elem interface{}) (uint64, error) {
   908  	converted, err := parseUint(elem, 64)
   909  	if err != nil {
   910  		return 0, err
   911  	}
   912  	return uint64(converted), nil
   913  }
   914  
   915  func getInt64(elem interface{}) (int64, error) {
   916  	converted, err := parseInt(elem, 64)
   917  	if err != nil {
   918  		return 0, err
   919  	}
   920  	return int64(converted), nil
   921  }
   922  
   923  var uintSize = 8 * int(unsafe.Sizeof(uint(0)))
   924  
   925  func getUint(elem interface{}) (uint, error) {
   926  	converted, err := parseUint(elem, uintSize)
   927  	if err != nil {
   928  		return 0, err
   929  	}
   930  	return uint(converted), nil
   931  }
   932  
   933  var intSize = 8 * int(unsafe.Sizeof(int(0)))
   934  
   935  func getInt(elem interface{}) (int, error) {
   936  	converted, err := parseInt(elem, intSize)
   937  	if err != nil {
   938  		return 0, err
   939  	}
   940  	return int(converted), nil
   941  }
   942  
   943  func parseInt(elem interface{}, bits int) (int64, error) {
   944  	switch casted := elem.(type) {
   945  	case bool:
   946  		if casted {
   947  			return 1, nil
   948  		}
   949  		return 0, nil
   950  	case []byte, float32, float64, WChar:
   951  		goto conversionErr
   952  	case int, uint, int8, int16, int32, int64, uint8, uint16, uint32, uint64:
   953  		goto fromString
   954  	case string:
   955  		goto fromString
   956  	default:
   957  		goto conversionErr
   958  	}
   959  conversionErr:
   960  	return 0, defaultFormatConversionError(elem, fmt.Sprintf("int%d", bits))
   961  fromString:
   962  	str := fmt.Sprint(elem)
   963  	i, err := strconv.ParseInt(str, 10, bits)
   964  	if err != nil {
   965  		return 0, nestedFormatConversionError(elem, fmt.Sprintf("int%d", bits), err)
   966  	}
   967  	return int64(i), nil
   968  }
   969  
   970  func parseUint(elem interface{}, bits int) (uint64, error) {
   971  	switch casted := elem.(type) {
   972  	case bool:
   973  		if casted {
   974  			return 1, nil
   975  		}
   976  		return 0, nil
   977  
   978  	case []byte, float32, float64, WChar:
   979  		goto conversionErr
   980  	case int, uint, int8, int16, int32, int64, uint8, uint16, uint32, uint64:
   981  		goto fromString
   982  	case string:
   983  		goto fromString
   984  	default:
   985  		goto conversionErr
   986  	}
   987  conversionErr:
   988  	return 0, defaultFormatConversionError(elem, fmt.Sprintf("uint%d", bits))
   989  fromString:
   990  	str := fmt.Sprint(elem)
   991  	i, err := strconv.ParseUint(str, 10, bits)
   992  	if err != nil {
   993  		return 0, nestedFormatConversionError(elem, fmt.Sprintf("uint%d", bits), err)
   994  	}
   995  	return uint64(i), nil
   996  }
   997  
   998  func getByteArray(elem interface{}) ([]byte, error) {
   999  	switch casted := elem.(type) {
  1000  	case []byte:
  1001  		return casted, nil
  1002  	case string:
  1003  		return []byte(casted), nil
  1004  	default:
  1005  		return nil, defaultFormatConversionError(elem, "[]uint8")
  1006  	}
  1007  }
  1008  
  1009  func getFloat32(elem interface{}) (float32, error) {
  1010  	const typeString = "float32"
  1011  	switch casted := elem.(type) {
  1012  	case float32:
  1013  		return casted, nil
  1014  	case string:
  1015  		f, err := strconv.ParseFloat(casted, 32)
  1016  		if err != nil {
  1017  			return 0, nestedFormatConversionError(casted, typeString, err)
  1018  		}
  1019  		return float32(f), nil
  1020  	default:
  1021  		return 0, defaultFormatConversionError(elem, typeString)
  1022  	}
  1023  }
  1024  
  1025  func getFloat64(elem interface{}) (float64, error) {
  1026  	const typeString = "float64"
  1027  	switch casted := elem.(type) {
  1028  	case float64:
  1029  		return casted, nil
  1030  	case string:
  1031  		f, err := strconv.ParseFloat(casted, 64)
  1032  		if err != nil {
  1033  			return 0, nestedFormatConversionError(casted, typeString, err)
  1034  		}
  1035  		return f, nil
  1036  	default:
  1037  		return 0, defaultFormatConversionError(elem, typeString)
  1038  	}
  1039  }
  1040  
  1041  func getString(elem interface{}) (string, error) {
  1042  	switch casted := elem.(type) {
  1043  	case string:
  1044  		return casted, nil
  1045  	default:
  1046  		return "", defaultFormatConversionError(elem, "string")
  1047  	}
  1048  }
  1049  
  1050  func getWChar(elem interface{}) (WChar, error) {
  1051  	switch casted := elem.(type) {
  1052  	case uint8:
  1053  		return WChar(casted), nil
  1054  	case int8:
  1055  		return WChar(casted), nil
  1056  	case WChar:
  1057  		return casted, nil
  1058  	case string:
  1059  		if len(casted) == 1 {
  1060  			return WChar(casted[0]), nil
  1061  		}
  1062  		return 0, &FormatConversionError{fmt.Sprintf("cannot convert string of length %d to sdt.WChar", len(casted)), casted}
  1063  	default:
  1064  		return 0, defaultFormatConversionError(elem, "sdt.WChar")
  1065  	}
  1066  }
  1067  
  1068  func getMap(elem interface{}) (Map, error) {
  1069  	if casted, ok := elem.(Map); ok {
  1070  		return casted, nil
  1071  	}
  1072  	return nil, defaultFormatConversionError(elem, "sdt.Map")
  1073  }
  1074  
  1075  func getStream(elem interface{}) (Stream, error) {
  1076  	if casted, ok := elem.(Stream); ok {
  1077  		return casted, nil
  1078  	}
  1079  	return nil, defaultFormatConversionError(elem, "sdt.Stream")
  1080  }
  1081  
  1082  func getQueue(elem interface{}) (*resource.Queue, error) {
  1083  	if casted, ok := elem.(*resource.Queue); ok {
  1084  		return casted, nil
  1085  	}
  1086  	return nil, defaultFormatConversionError(elem, "*resource.Queue")
  1087  }
  1088  
  1089  func getTopic(elem interface{}) (*resource.Topic, error) {
  1090  	if casted, ok := elem.(*resource.Topic); ok {
  1091  		return casted, nil
  1092  	}
  1093  	return nil, defaultFormatConversionError(elem, "*resource.Topic")
  1094  }
  1095  
  1096  func getDestination(elem interface{}) (resource.Destination, error) {
  1097  	if casted, ok := elem.(resource.Destination); ok {
  1098  		return casted, nil
  1099  	}
  1100  	return nil, defaultFormatConversionError(elem, "resource.Destination")
  1101  }
  1102