Skip to content

Commit 514b911

Browse files
authored
feat: Read JSON AST models into memory (#1010)
2 parents 3b08352 + d5a2766 commit 514b911

File tree

10 files changed

+220
-6
lines changed

10 files changed

+220
-6
lines changed

Package.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,9 +303,13 @@ let package = Package(
303303
.executableTarget(
304304
name: "SmithyCodegenCLI",
305305
dependencies: [
306+
"SmithyCodegenCore",
306307
.product(name: "ArgumentParser", package: "swift-argument-parser"),
307308
]
308309
),
310+
.target(
311+
name: "SmithyCodegenCore"
312+
),
309313
.testTarget(
310314
name: "ClientRuntimeTests",
311315
dependencies: [

Sources/SmithyCodegenCLI/SmithyCodegenCLI.swift

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import ArgumentParser
99
import Foundation
10+
import struct SmithyCodegenCore.CodeGenerator
1011

1112
@main
1213
struct SmithyCodegenCLI: AsyncParsableCommand {
@@ -17,6 +18,9 @@ struct SmithyCodegenCLI: AsyncParsableCommand {
1718
@Option(help: "The full or relative path to write the schemas output file.")
1819
var schemasPath: String?
1920

21+
@Option(help: "The full or relative path to write the struct consumers output file.")
22+
var structConsumersPath: String?
23+
2024
func run() async throws {
2125
let currentWorkingDirectoryFileURL = currentWorkingDirectoryFileURL()
2226
print("Current working directory: \(currentWorkingDirectoryFileURL.path)")
@@ -31,12 +35,11 @@ struct SmithyCodegenCLI: AsyncParsableCommand {
3135
// If --schemas-path was supplied, create the schema file URL
3236
let schemasFileURL = resolve(paramName: "--schemas-path", path: schemasPath)
3337

34-
// All file URLs needed for code generation have now been resolved.
35-
// Implement code generation here.
36-
if let schemasFileURL {
37-
print("Schemas file path: \(schemasFileURL)")
38-
FileManager.default.createFile(atPath: schemasFileURL.path, contents: Data())
39-
}
38+
// Use resolved file URLs to run code generator
39+
try CodeGenerator(
40+
modelFileURL: modelFileURL,
41+
schemasFileURL: schemasFileURL
42+
).run()
4043
}
4144

4245
private func currentWorkingDirectoryFileURL() -> URL {
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//
2+
// Copyright Amazon.com Inc. or its affiliates.
3+
// All Rights Reserved.
4+
//
5+
// SPDX-License-Identifier: Apache-2.0
6+
//
7+
8+
public struct ASTError: Error {
9+
public let localizedDescription: String
10+
11+
init(_ localizedDescription: String) {
12+
self.localizedDescription = localizedDescription
13+
}
14+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//
2+
// Copyright Amazon.com Inc. or its affiliates.
3+
// All Rights Reserved.
4+
//
5+
// SPDX-License-Identifier: Apache-2.0
6+
//
7+
8+
// See https://smithy.io/2.0/spec/json-ast.html#ast-member
9+
struct ASTMember: Decodable {
10+
let target: String
11+
let traits: [String: ASTNode]?
12+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//
2+
// Copyright Amazon.com Inc. or its affiliates.
3+
// All Rights Reserved.
4+
//
5+
// SPDX-License-Identifier: Apache-2.0
6+
//
7+
8+
// See https://smithy.io/2.0/spec/json-ast.html#top-level-properties
9+
struct ASTModel: Decodable {
10+
let smithy: String
11+
let metadata: ASTNode?
12+
let shapes: [String: ASTShape]
13+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//
2+
// Copyright Amazon.com Inc. or its affiliates.
3+
// All Rights Reserved.
4+
//
5+
// SPDX-License-Identifier: Apache-2.0
6+
//
7+
8+
/// Contains the value of a Smithy Node, as used in a JSON AST.
9+
///
10+
/// Smithy node data is basically the same as the data that can be stored in JSON.
11+
/// The root of a Smithy node may be of any type, i.e. unlike JSON, the root element is not limited to object or list.
12+
///
13+
/// See the definition of node value in the Smithy spec: https://smithy.io/2.0/spec/model.html#node-values
14+
enum ASTNode {
15+
case object([String: ASTNode])
16+
case list([ASTNode])
17+
case string(String)
18+
case number(Double)
19+
case boolean(Bool)
20+
case null
21+
}
22+
23+
extension ASTNode: Decodable {
24+
25+
init(from decoder: any Decoder) throws {
26+
let container = try decoder.singleValueContainer()
27+
if container.decodeNil() {
28+
self = .null
29+
} else if let bool = try? container.decode(Bool.self) {
30+
self = .boolean(bool)
31+
} else if let int = try? container.decode(Int.self) {
32+
self = .number(Double(int))
33+
} else if let double = try? container.decode(Double.self) {
34+
self = .number(double)
35+
} else if let string = try? container.decode(String.self) {
36+
self = .string(string)
37+
} else if let array = try? container.decode([ASTNode].self) {
38+
self = .list(array)
39+
} else if let dictionary = try? container.decode([String: ASTNode].self) {
40+
self = .object(dictionary)
41+
} else {
42+
throw ASTError("Undecodable value in AST node")
43+
}
44+
}
45+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
//
2+
// Copyright Amazon.com Inc. or its affiliates.
3+
// All Rights Reserved.
4+
//
5+
// SPDX-License-Identifier: Apache-2.0
6+
//
7+
8+
// See https://smithy.io/2.0/spec/json-ast.html#ast-shape-reference
9+
struct ASTReference: Decodable {
10+
let target: String
11+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//
2+
// Copyright Amazon.com Inc. or its affiliates.
3+
// All Rights Reserved.
4+
//
5+
// SPDX-License-Identifier: Apache-2.0
6+
//
7+
8+
// See https://smithy.io/2.0/spec/json-ast.html#json-ast
9+
// This Swift type captures fields for all AST shape types
10+
struct ASTShape: Decodable {
11+
let type: ASTType
12+
let traits: [String: ASTNode]?
13+
let member: ASTMember?
14+
let key: ASTMember?
15+
let value: ASTMember?
16+
let members: [String: ASTMember]?
17+
let version: String?
18+
let operations: [ASTReference]?
19+
let resources: [ASTReference]?
20+
let errors: [ASTReference]?
21+
let rename: [String: String]?
22+
let identifiers: [String: ASTReference]?
23+
let properties: [String: ASTReference]?
24+
let create: ASTReference?
25+
let put: ASTReference?
26+
let read: ASTReference?
27+
let update: ASTReference?
28+
let delete: ASTReference?
29+
let list: ASTReference?
30+
let collectionOperations: [ASTReference]?
31+
let input: ASTReference?
32+
let output: ASTReference?
33+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//
2+
// Copyright Amazon.com Inc. or its affiliates.
3+
// All Rights Reserved.
4+
//
5+
// SPDX-License-Identifier: Apache-2.0
6+
//
7+
8+
// See https://smithy.io/2.0/spec/model.html#shape-types
9+
enum ASTType: String, Decodable {
10+
// These cases are all the standard Smithy shape types
11+
case blob
12+
case boolean
13+
case string
14+
case timestamp
15+
case byte
16+
case short
17+
case integer
18+
case long
19+
case float
20+
case document
21+
case double
22+
case bigDecimal
23+
case bigInteger
24+
case `enum`
25+
case intEnum
26+
case list
27+
case set
28+
case map
29+
case structure
30+
case union
31+
case member
32+
case service
33+
case resource
34+
case operation
35+
36+
// Special for AST, added 'apply' case
37+
case apply
38+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//
2+
// Copyright Amazon.com Inc. or its affiliates.
3+
// All Rights Reserved.
4+
//
5+
// SPDX-License-Identifier: Apache-2.0
6+
//
7+
8+
import struct Foundation.Data
9+
import class Foundation.FileManager
10+
import class Foundation.JSONDecoder
11+
import struct Foundation.URL
12+
13+
public struct CodeGenerator {
14+
let modelFileURL: URL
15+
let schemasFileURL: URL?
16+
17+
public init(
18+
modelFileURL: URL,
19+
schemasFileURL: URL?
20+
) {
21+
self.modelFileURL = modelFileURL
22+
self.schemasFileURL = schemasFileURL
23+
}
24+
25+
public func run() throws {
26+
// Load the AST from the model file
27+
let modelData = try Data(contentsOf: modelFileURL)
28+
let astModel = try JSONDecoder().decode(ASTModel.self, from: modelData)
29+
30+
// In the future, AST will be used to create a Model.
31+
// Model will be used to generate code.
32+
33+
// This code simply writes an empty schemas file, since it is expected to exist after the
34+
// code generator plugin runs.
35+
//
36+
// Actual code generation will be implemented here later.
37+
if let schemasFileURL {
38+
FileManager.default.createFile(atPath: schemasFileURL.path, contents: Data())
39+
}
40+
}
41+
}

0 commit comments

Comments
 (0)