Draft smithy parser for AWS models

This commit is contained in:
benduncan
2025-08-09 13:37:12 +08:00
parent 19003b7531
commit 8fadebf64d
+300
View File
@@ -0,0 +1,300 @@
package main
import (
"encoding/json"
"flag"
"fmt"
"io"
"log"
"os"
"strings"
"github.com/davecgh/go-spew/spew"
)
// Parse AWS Smithy models
type Smithy struct {
Smithy string `json:"smithy"`
Metadata map[string]interface{}
Shapes map[string]interface{}
}
type SmithyRange struct {
Min int `json:"min"`
Max int `json:"max"`
}
type SmithyPaginated struct {
InputToken string `json:"inputToken"`
OutputToken string `json:"outputToken"`
Items string `json:"items"`
PageSize string `json:"pageSize"`
}
type SmithyTrait struct {
Documentation string `json:"smithy.api#documentation"`
XMLName string `json:"smithy.api#xmlName"`
Ec2QueryName string `json:"smithy.api#ec2QueryName"`
Range SmithyRange `json:"smithy.api#range"` // optional
SmithyPaginated SmithyPaginated `json:"smithy.api#paginated"` // optional
}
type SmithyMember struct {
Target string `json:"target"`
Traits SmithyTrait `json:"traits"`
}
type SmithyMemberStructure struct {
Target string `json:"target"`
Traits SmithyTrait `json:"traits"`
}
type SmithyRequest struct {
Type string `json:"type"`
}
type SmithyRequestList struct {
Type string `json:"type"`
Member SmithyMember `json:"member"`
}
type SmithyRequestStructure struct {
Type string `json:"type"`
Members map[string]SmithyMemberStructure `json:"members"`
Traits SmithyTrait `json:"traits"`
}
var result Smithy
var smithyTypes = map[string]string{
"String": "string",
"Integer": "int",
"Boolean": "bool",
"DateTime": "time.Time",
}
func main() {
path := flag.String("path", "", "Path to the Smithy model")
flag.Parse()
if path == nil {
log.Fatal("path is required")
}
// Open the file and read JSON
jsonFile, err := os.Open(*path)
if err != nil {
log.Fatal(err)
}
defer jsonFile.Close()
// Read the file and unmarshal as JSON
byteValue, _ := io.ReadAll(jsonFile)
json.Unmarshal([]byte(byteValue), &result)
// spew.Dump(result)
_, req := findShape("com.amazonaws.ec2#DescribeInstancesRequest")
scanRequest("com.amazonaws.ec2#DescribeInstancesRequest", req, true)
/*
for k, v := range result.Shapes {
// Find all request methods
// "com.amazonaws.ec2#RunInstancesRequest"
// DescribeInstancesRequest
if strings.HasSuffix(k, "DescribeInstancesRequest") {
//fmt.Println(k)
scanRequest(k, v.(interface{}), true)
// Loop in each request
//for k2, v2 := range v.(map[string]interface{}) {
// fmt.Println(k2, v2)
//}
}
}
*/
}
func scanRequest(key string, request interface{}, root bool) {
//spew.Dump(request)
// Marshal into a SmithyRequest
// Unmarshal into a SmithyRequest
var smithyRequest SmithyRequest
b, _ := json.Marshal(request) // map -> JSON
_ = json.Unmarshal(b, &smithyRequest) // JSON -> struct
//spew.Dump(smithyRequest)
keyName := strings.Split(key, "#")
if len(keyName) == 0 {
fmt.Println("No key found")
return
}
if root {
fmt.Printf("type %s struct {\n", keyName[1])
}
if smithyRequest.Type == "structure" {
findShapeRecursiveStructure(request)
} else if smithyRequest.Type == "list" {
findShapeRecursiveList(request)
}
//fmt.Println(smithyRequest.Type)
//for k, v := range request {
// fmt.Println(k, v)
//}
}
func findShapeRecursiveList(request interface{}) error {
var smithyRequest SmithyRequestList
b, _ := json.Marshal(request) // map -> JSON
_ = json.Unmarshal(b, &smithyRequest) // JSON -> struct
fmt.Println("findShapeRecursiveList:")
spew.Dump(request)
spew.Dump(smithyRequest)
memberType := strings.Split(smithyRequest.Member.Target, "#")
fmt.Println(memberType)
if len(memberType) == 0 {
fmt.Println("No member type found")
return nil
}
typeName := memberType[1]
// If a matching smithyType
goType := smithyTypes[typeName]
if goType != "" {
fmt.Printf("%s %s `xml:\"%s\"`\n", goType, smithyRequest.Member.Traits.XMLName)
} else {
//spew.Dump(v)
fmt.Println("\t", smithyRequest.Member.Target, smithyRequest.Member.Traits.XMLName)
newSmithyRequest, request := findShape(smithyRequest.Member.Target)
if newSmithyRequest.Type == "structure" {
findShapeRecursiveStructure(request)
} else if newSmithyRequest.Type == "list" {
findShapeRecursiveList(request)
}
//findShapeRecursiveStructure(newSmithyRequest)
}
//fmt.Println(smithyRequest.Members)
fmt.Println()
return nil
}
func findShapeRecursiveStructure(request interface{}) error {
fmt.Println("findShapeRecursiveStructure => \t")
var smithyRequest SmithyRequestStructure
b, _ := json.Marshal(request) // map -> JSON
_ = json.Unmarshal(b, &smithyRequest) // JSON -> struct
for k, v := range smithyRequest.Members {
//fmt.Println("\t", k, v)
memberType := strings.Split(v.Target, "#")
fmt.Println(memberType)
if len(memberType) == 0 {
fmt.Println("No member type found")
continue
}
typeName := memberType[1]
// If a matching smithyType
goType := smithyTypes[typeName]
if goType != "" {
fmt.Printf("%s %s `xml:\"%s\"`\n", k, goType, v.Traits.XMLName)
} else {
//spew.Dump(v)
//fmt.Println("\t", k, v.Target, v.Traits.XMLName)
newSmithyRequest, request := findShape(v.Target)
if newSmithyRequest.Type == "structure" {
findShapeRecursiveStructure(request)
} else if newSmithyRequest.Type == "list" {
findShapeRecursiveList(request)
}
//findShapeRecursiveStructure(newSmithyRequest)
}
}
//fmt.Println(smithyRequest.Members)
fmt.Println()
return nil
}
func findShape(key string) (SmithyRequest, interface{}) {
for k, v := range result.Shapes {
if k == key {
//fmt.Println("MATCH", k, key)
//spew.Dump(v)
//fmt.Println(k, v)
var smithyRequest SmithyRequest
b, _ := json.Marshal(v) // map -> JSON
_ = json.Unmarshal(b, &smithyRequest) // JSON -> struct
//spew.Dump(v)
//spew.Dump(smithyRequest)
return smithyRequest, v
}
}
return SmithyRequest{}, nil
}