From 8fadebf64d6cb3adc19fa6cf1e3d3e72333adeac Mon Sep 17 00:00:00 2001 From: benduncan <5178243+benduncan@users.noreply.github.com> Date: Sat, 9 Aug 2025 13:37:12 +0800 Subject: [PATCH] Draft smithy parser for AWS models --- cmd/smithy/main.go | 300 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 300 insertions(+) create mode 100644 cmd/smithy/main.go diff --git a/cmd/smithy/main.go b/cmd/smithy/main.go new file mode 100644 index 000000000..fdc57f6f1 --- /dev/null +++ b/cmd/smithy/main.go @@ -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 + +}