github.com/aavshr/aws-sdk-go@v1.41.3/private/model/api/shape_value_builder.go (about)

     1  //go:build codegen
     2  // +build codegen
     3  
     4  package api
     5  
     6  import (
     7  	"encoding/base64"
     8  	"encoding/json"
     9  	"fmt"
    10  	"reflect"
    11  	"sort"
    12  	"strings"
    13  
    14  	"github.com/aavshr/aws-sdk-go/aws"
    15  	"github.com/aavshr/aws-sdk-go/private/protocol"
    16  )
    17  
    18  // ShapeValueBuilder provides the logic to build the nested values for a shape.
    19  // Base64BlobValues is true if the blob field in shapeRef.Shape.Type is base64
    20  // encoded.
    21  type ShapeValueBuilder struct {
    22  	// Specifies if API shapes modeled as blob types, input values are base64
    23  	// encoded or not, and strings values instead.
    24  	Base64BlobValues bool
    25  
    26  	// The helper that will provide the logic and formated code to convert a
    27  	// timestamp input value into a Go time.Time.
    28  	ParseTimeString func(ref *ShapeRef, memberName, v string) string
    29  }
    30  
    31  // NewShapeValueBuilder returns an initialized ShapeValueBuilder for generating
    32  // API shape types initialized with values.
    33  func NewShapeValueBuilder() ShapeValueBuilder {
    34  	return ShapeValueBuilder{ParseTimeString: parseUnixTimeString}
    35  }
    36  
    37  // BuildShape will recursively build the referenced shape based on the json
    38  // object provided.  isMap will dictate how the field name is specified. If
    39  // isMap is true, we will expect the member name to be quotes like "Foo".
    40  func (b ShapeValueBuilder) BuildShape(ref *ShapeRef, shapes map[string]interface{}, isMap bool) string {
    41  	order := make([]string, len(shapes))
    42  	for k := range shapes {
    43  		order = append(order, k)
    44  	}
    45  	sort.Strings(order)
    46  
    47  	ret := ""
    48  	for _, name := range order {
    49  		if name == "" {
    50  			continue
    51  		}
    52  		shape := shapes[name]
    53  
    54  		// If the shape isn't a map, we want to export the value, since every field
    55  		// defined in our shapes are exported.
    56  		if len(name) > 0 && !isMap && strings.ToLower(name[0:1]) == name[0:1] {
    57  			name = strings.Title(name)
    58  		}
    59  
    60  		memName := name
    61  		passRef := ref.Shape.MemberRefs[name]
    62  		if isMap {
    63  			memName = fmt.Sprintf("%q", memName)
    64  			passRef = &ref.Shape.ValueRef
    65  		}
    66  		switch v := shape.(type) {
    67  		case map[string]interface{}:
    68  			ret += b.BuildComplex(name, memName, passRef, ref.Shape, v)
    69  		case []interface{}:
    70  			ret += b.BuildList(name, memName, passRef, v)
    71  		default:
    72  
    73  			ret += b.BuildScalar(name, memName, passRef, v, ref.Shape.Payload == name)
    74  		}
    75  	}
    76  	return ret
    77  }
    78  
    79  // BuildList will construct a list shape based off the service's definition of
    80  // that list.
    81  func (b ShapeValueBuilder) BuildList(name, memName string, ref *ShapeRef, v []interface{}) string {
    82  	ret := ""
    83  
    84  	if len(v) == 0 || ref == nil {
    85  		return ""
    86  	}
    87  
    88  	passRef := &ref.Shape.MemberRef
    89  	ret += fmt.Sprintf("%s: %s {\n", memName, b.GoType(ref, false))
    90  	ret += b.buildListElements(passRef, v)
    91  	ret += "},\n"
    92  	return ret
    93  }
    94  
    95  func (b ShapeValueBuilder) buildListElements(ref *ShapeRef, v []interface{}) string {
    96  	if len(v) == 0 || ref == nil {
    97  		return ""
    98  	}
    99  
   100  	ret := ""
   101  	format := ""
   102  	isComplex := false
   103  	isList := false
   104  
   105  	// get format for atomic type. If it is not an atomic type,
   106  	// get the element.
   107  	switch v[0].(type) {
   108  	case string:
   109  		format = "%s"
   110  	case bool:
   111  		format = "%t"
   112  	case float64:
   113  		switch ref.Shape.Type {
   114  		case "integer", "int64", "long":
   115  			format = "%d"
   116  		default:
   117  			format = "%f"
   118  		}
   119  	case []interface{}:
   120  		isList = true
   121  	case map[string]interface{}:
   122  		isComplex = true
   123  	}
   124  
   125  	for _, elem := range v {
   126  		if isComplex {
   127  			ret += fmt.Sprintf("{\n%s\n},\n", b.BuildShape(ref, elem.(map[string]interface{}), ref.Shape.Type == "map"))
   128  		} else if isList {
   129  			ret += fmt.Sprintf("{\n%s\n},\n", b.buildListElements(&ref.Shape.MemberRef, elem.([]interface{})))
   130  		} else {
   131  			switch ref.Shape.Type {
   132  			case "integer", "int64", "long":
   133  				elem = int(elem.(float64))
   134  			}
   135  			ret += fmt.Sprintf("%s,\n", getValue(ref.Shape.Type, fmt.Sprintf(format, elem)))
   136  		}
   137  	}
   138  	return ret
   139  }
   140  
   141  // BuildScalar will build atomic Go types.
   142  func (b ShapeValueBuilder) BuildScalar(name, memName string, ref *ShapeRef, shape interface{}, isPayload bool) string {
   143  	if ref == nil || ref.Shape == nil {
   144  		return ""
   145  	}
   146  
   147  	switch v := shape.(type) {
   148  	case bool:
   149  		return convertToCorrectType(memName, ref.Shape.Type, fmt.Sprintf("%t", v))
   150  	case int:
   151  		if ref.Shape.Type == "timestamp" {
   152  			return b.ParseTimeString(ref, memName, fmt.Sprintf("%d", v))
   153  		}
   154  		return convertToCorrectType(memName, ref.Shape.Type, fmt.Sprintf("%d", v))
   155  	case float64:
   156  
   157  		dataType := ref.Shape.Type
   158  
   159  		if dataType == "timestamp" {
   160  			return b.ParseTimeString(ref, memName, fmt.Sprintf("%f", v))
   161  		}
   162  		if dataType == "integer" || dataType == "int64" || dataType == "long" {
   163  			return convertToCorrectType(memName, ref.Shape.Type, fmt.Sprintf("%d", int(shape.(float64))))
   164  		}
   165  		return convertToCorrectType(memName, ref.Shape.Type, fmt.Sprintf("%f", v))
   166  	case string:
   167  		t := ref.Shape.Type
   168  		switch t {
   169  		case "timestamp":
   170  			return b.ParseTimeString(ref, memName, fmt.Sprintf("%s", v))
   171  
   172  		case "jsonvalue":
   173  			return fmt.Sprintf("%s: %#v,\n", memName, parseJSONString(v))
   174  
   175  		case "blob":
   176  			if (ref.Streaming || ref.Shape.Streaming) && isPayload {
   177  				return fmt.Sprintf("%s: aws.ReadSeekCloser(strings.NewReader(%q)),\n", memName, v)
   178  			}
   179  			if b.Base64BlobValues {
   180  				decodedBlob, err := base64.StdEncoding.DecodeString(v)
   181  				if err != nil {
   182  					panic(fmt.Errorf("Failed to decode string: %v", err))
   183  				}
   184  				return fmt.Sprintf("%s: []byte(%q),\n", memName, decodedBlob)
   185  			}
   186  			return fmt.Sprintf("%s: []byte(%q),\n", memName, v)
   187  		default:
   188  			return convertToCorrectType(memName, t, v)
   189  		}
   190  	default:
   191  		panic(fmt.Errorf("Unsupported scalar type: %v", reflect.TypeOf(v)))
   192  	}
   193  }
   194  
   195  // BuildComplex will build the shape's value for complex types such as structs,
   196  // and maps.
   197  func (b ShapeValueBuilder) BuildComplex(name, memName string, ref *ShapeRef, parent *Shape, v map[string]interface{}) string {
   198  	switch parent.Type {
   199  	case "structure":
   200  		if ref.Shape.Type == "map" {
   201  			return fmt.Sprintf(`%s: %s{
   202  				%s
   203  			},
   204  			`, memName, b.GoType(ref, true), b.BuildShape(ref, v, true))
   205  		} else {
   206  			// Don't try to generate for members not actually modeled in the API
   207  			if _, ok := parent.MemberRefs[memName]; !ok {
   208  				return ""
   209  			}
   210  			return fmt.Sprintf(`%s: &%s{
   211  				%s
   212  			},
   213  			`, memName, b.GoType(ref, true), b.BuildShape(ref, v, false))
   214  		}
   215  	case "map":
   216  		if ref.Shape.Type == "map" {
   217  			return fmt.Sprintf(`%q: %s{
   218  				%s
   219  			},
   220  			`, name, b.GoType(ref, false), b.BuildShape(ref, v, true))
   221  		} else {
   222  			return fmt.Sprintf(`%s: &%s{
   223  				%s
   224  			},
   225  			`, memName, b.GoType(ref, true), b.BuildShape(ref, v, false))
   226  		}
   227  	default:
   228  		panic(fmt.Sprintf("Expected complex type but received %q", ref.Shape.Type))
   229  	}
   230  }
   231  
   232  // GoType returns the string of the shape's Go type identifier.
   233  func (b ShapeValueBuilder) GoType(ref *ShapeRef, elem bool) string {
   234  
   235  	if ref.Shape.Type != "structure" && ref.Shape.Type != "list" && ref.Shape.Type != "map" {
   236  		// Scalars are always pointers.
   237  		return ref.GoTypeWithPkgName()
   238  	}
   239  
   240  	prefix := ""
   241  	if ref.Shape.Type == "list" {
   242  		ref = &ref.Shape.MemberRef
   243  		prefix = "[]"
   244  	}
   245  
   246  	if elem {
   247  		return prefix + ref.Shape.GoTypeWithPkgNameElem()
   248  	}
   249  	return prefix + ref.GoTypeWithPkgName()
   250  }
   251  
   252  // parseJSONString a json string and returns aws.JSONValue.
   253  func parseJSONString(input string) aws.JSONValue {
   254  	var v aws.JSONValue
   255  	if err := json.Unmarshal([]byte(input), &v); err != nil {
   256  		panic(fmt.Sprintf("unable to unmarshal JSONValue, %v", err))
   257  	}
   258  	return v
   259  }
   260  
   261  // InlineParseModeledTime returns the string of an inline function which
   262  // returns time.
   263  func inlineParseModeledTime(format, v string) string {
   264  	const formatTimeTmpl = `func() *time.Time{
   265          v, err := protocol.ParseTime("%s", "%s")
   266          if err != nil {
   267              panic(err)
   268          }
   269          return &v
   270      }()`
   271  
   272  	return fmt.Sprintf(formatTimeTmpl, format, v)
   273  }
   274  
   275  // parseUnixTimeString returns a string which assigns the value of a time
   276  // member using an inline function Defined inline function parses time in
   277  // UnixTimeFormat.
   278  func parseUnixTimeString(ref *ShapeRef, memName, v string) string {
   279  	ref.API.AddSDKImport("private/protocol")
   280  	return fmt.Sprintf("%s: %s,\n", memName, inlineParseModeledTime(protocol.UnixTimeFormatName, v))
   281  }