Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -303,9 +303,13 @@ let package = Package(
.executableTarget(
name: "SmithyCodegenCLI",
dependencies: [
"SmithyCodegenCore",
.product(name: "ArgumentParser", package: "swift-argument-parser"),
]
),
.target(
name: "SmithyCodegenCore"
),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created this target for codegen components.

Eventually this may be exposed as a library, but for now it's solely a smithy-swift internal target.

.testTarget(
name: "ClientRuntimeTests",
dependencies: [
Expand Down
15 changes: 9 additions & 6 deletions Sources/SmithyCodegenCLI/SmithyCodegenCLI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import ArgumentParser
import Foundation
import struct SmithyCodegenCore.CodeGenerator

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

@Option(help: "The full or relative path to write the struct consumers output file.")
var structConsumersPath: String?

func run() async throws {
let currentWorkingDirectoryFileURL = currentWorkingDirectoryFileURL()
print("Current working directory: \(currentWorkingDirectoryFileURL.path)")
Expand All @@ -31,12 +35,11 @@ struct SmithyCodegenCLI: AsyncParsableCommand {
// If --schemas-path was supplied, create the schema file URL
let schemasFileURL = resolve(paramName: "--schemas-path", path: schemasPath)

// All file URLs needed for code generation have now been resolved.
// Implement code generation here.
if let schemasFileURL {
print("Schemas file path: \(schemasFileURL)")
FileManager.default.createFile(atPath: schemasFileURL.path, contents: Data())
}
// Use resolved file URLs to run code generator
try CodeGenerator(
modelFileURL: modelFileURL,
schemasFileURL: schemasFileURL
).run()
}

private func currentWorkingDirectoryFileURL() -> URL {
Expand Down
14 changes: 14 additions & 0 deletions Sources/SmithyCodegenCore/AST/ASTError.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This error just get thrown with an appropriate message if there is an error while reading an AST.

public struct ASTError: Error {
public let localizedDescription: String

init(_ localizedDescription: String) {
self.localizedDescription = localizedDescription
}
}
12 changes: 12 additions & 0 deletions Sources/SmithyCodegenCore/AST/ASTMember.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This and the below AST... files are just containers for the JSON data read from the AST file.


// See https://smithy.io/2.0/spec/json-ast.html#ast-member
struct ASTMember: Decodable {
let target: String
let traits: [String: ASTNode]?
}
13 changes: 13 additions & 0 deletions Sources/SmithyCodegenCore/AST/ASTModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

// See https://smithy.io/2.0/spec/json-ast.html#top-level-properties
struct ASTModel: Decodable {
let smithy: String
let metadata: ASTNode?
let shapes: [String: ASTShape]
}
45 changes: 45 additions & 0 deletions Sources/SmithyCodegenCore/AST/ASTNode.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

/// Contains the value of a Smithy Node, as used in a JSON AST.
///
/// Smithy node data is basically the same as the data that can be stored in JSON.
/// 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.
///
/// See the definition of node value in the Smithy spec: https://smithy.io/2.0/spec/model.html#node-values
enum ASTNode {
case object([String: ASTNode])
case list([ASTNode])
case string(String)
case number(Double)
case boolean(Bool)
case null
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because a node in AST can be of multiple JSON types, the hand-written Decodable implementation below is provided to capture any one of them.

extension ASTNode: Decodable {

init(from decoder: any Decoder) throws {
let container = try decoder.singleValueContainer()
if container.decodeNil() {
self = .null
} else if let bool = try? container.decode(Bool.self) {
self = .boolean(bool)
} else if let int = try? container.decode(Int.self) {
self = .number(Double(int))
} else if let double = try? container.decode(Double.self) {
self = .number(double)
} else if let string = try? container.decode(String.self) {
self = .string(string)
} else if let array = try? container.decode([ASTNode].self) {
self = .list(array)
} else if let dictionary = try? container.decode([String: ASTNode].self) {
self = .object(dictionary)
} else {
throw ASTError("Undecodable value in AST node")
}
}
}
11 changes: 11 additions & 0 deletions Sources/SmithyCodegenCore/AST/ASTReference.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

// See https://smithy.io/2.0/spec/json-ast.html#ast-shape-reference
struct ASTReference: Decodable {
let target: String
}
33 changes: 33 additions & 0 deletions Sources/SmithyCodegenCore/AST/ASTShape.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than have multiple AST shape types, for simplicity all AST shapes are read with this one ASTShape type that has all the fields of every AST shape.

When constructing the actual model from AST later, the ASTShape's type field will be used to create custom shape types and fill those types' specific fields.


// See https://smithy.io/2.0/spec/json-ast.html#json-ast
// This Swift type captures fields for all AST shape types
struct ASTShape: Decodable {
let type: ASTType
let traits: [String: ASTNode]?
let member: ASTMember?
let key: ASTMember?
let value: ASTMember?
let members: [String: ASTMember]?
let version: String?
let operations: [ASTReference]?
let resources: [ASTReference]?
let errors: [ASTReference]?
let rename: [String: String]?
let identifiers: [String: ASTReference]?
let properties: [String: ASTReference]?
let create: ASTReference?
let put: ASTReference?
let read: ASTReference?
let update: ASTReference?
let delete: ASTReference?
let list: ASTReference?
let collectionOperations: [ASTReference]?
let input: ASTReference?
let output: ASTReference?
}
38 changes: 38 additions & 0 deletions Sources/SmithyCodegenCore/AST/ASTType.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

// See https://smithy.io/2.0/spec/model.html#shape-types
enum ASTType: String, Decodable {
// These cases are all the standard Smithy shape types
case blob
case boolean
case string
case timestamp
case byte
case short
case integer
case long
case float
case document
case double
case bigDecimal
case bigInteger
case `enum`
case intEnum
case list
case set
case map
case structure
case union
case member
case service
case resource
case operation

// Special for AST, added 'apply' case
case apply
}
41 changes: 41 additions & 0 deletions Sources/SmithyCodegenCore/CodeGenerator.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

import struct Foundation.Data
import class Foundation.FileManager
import class Foundation.JSONDecoder
import struct Foundation.URL

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This type is created & called by the CLI to actually perform the codegen.

Its responsibilities

  • Read the AST from the modelFileURL (done below)
  • Create a model, generation context, symbol provider, etc. (TBD)
  • Use the generation context to create the needed source files (TBD)

For now, it just creates an empty file at the schemas path, which is required for the build plugin to succeed.

public struct CodeGenerator {
let modelFileURL: URL
let schemasFileURL: URL?

public init(
modelFileURL: URL,
schemasFileURL: URL?
) {
self.modelFileURL = modelFileURL
self.schemasFileURL = schemasFileURL
}

public func run() throws {
// Load the AST from the model file
let modelData = try Data(contentsOf: modelFileURL)
let astModel = try JSONDecoder().decode(ASTModel.self, from: modelData)

Check warning on line 28 in Sources/SmithyCodegenCore/CodeGenerator.swift

View workflow job for this annotation

GitHub Actions / apple-ci (macos-14, Xcode_15.2, platform=tvOS Simulator,OS=17.2,name=Apple TV 4K (3rd generation)...

initialization of immutable value 'astModel' was never used; consider replacing with assignment to '_' or removing it

Check warning on line 28 in Sources/SmithyCodegenCore/CodeGenerator.swift

View workflow job for this annotation

GitHub Actions / apple-ci (macos-14, Xcode_15.2, platform=macOS)

initialization of immutable value 'astModel' was never used; consider replacing with assignment to '_' or removing it

Check warning on line 28 in Sources/SmithyCodegenCore/CodeGenerator.swift

View workflow job for this annotation

GitHub Actions / apple-ci (macos-14, Xcode_15.2, platform=iOS Simulator,OS=17.2,name=iPhone 15)

initialization of immutable value 'astModel' was never used; consider replacing with assignment to '_' or removing it

Check warning on line 28 in Sources/SmithyCodegenCore/CodeGenerator.swift

View workflow job for this annotation

GitHub Actions / apple-ci (macos-14, Xcode_15.2, platform=visionOS Simulator,OS=1.0,name=Apple Vision Pro)

initialization of immutable value 'astModel' was never used; consider replacing with assignment to '_' or removing it

Check warning on line 28 in Sources/SmithyCodegenCore/CodeGenerator.swift

View workflow job for this annotation

GitHub Actions / apple-ci (macos-26, Xcode_26.0, platform=tvOS Simulator,OS=26.0,name=Apple TV 4K (3rd generation)...

initialization of immutable value 'astModel' was never used; consider replacing with assignment to '_' or removing it

Check warning on line 28 in Sources/SmithyCodegenCore/CodeGenerator.swift

View workflow job for this annotation

GitHub Actions / apple-ci (macos-26, Xcode_26.0, platform=macOS)

initialization of immutable value 'astModel' was never used; consider replacing with assignment to '_' or removing it

Check warning on line 28 in Sources/SmithyCodegenCore/CodeGenerator.swift

View workflow job for this annotation

GitHub Actions / apple-ci (macos-26, Xcode_26.0, platform=iOS Simulator,OS=26.0.1,name=iPhone 17)

initialization of immutable value 'astModel' was never used; consider replacing with assignment to '_' or removing it

Check warning on line 28 in Sources/SmithyCodegenCore/CodeGenerator.swift

View workflow job for this annotation

GitHub Actions / apple-ci (macos-26, Xcode_26.0, platform=visionOS Simulator,OS=26.0,name=Apple Vision Pro)

initialization of immutable value 'astModel' was never used; consider replacing with assignment to '_' or removing it
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does look like astModel is never used as mentioned by the automated warning. What is it for?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the comments immediately below. In a follow-up PR, ASTModel will be used to construct the actual model that is used for codegen.


// In the future, AST will be used to create a Model.
// Model will be used to generate code.

// This code simply writes an empty schemas file, since it is expected to exist after the
// code generator plugin runs.
//
// Actual code generation will be implemented here later.
if let schemasFileURL {
FileManager.default.createFile(atPath: schemasFileURL.path, contents: Data())
}
}
}
Loading