Skip to content

Commit

Permalink
feat: Code-generate Acceptors for waiters (#483)
Browse files Browse the repository at this point in the history
  • Loading branch information
jbelkins authored Dec 16, 2022
1 parent e2b95fa commit 4dfe803
Show file tree
Hide file tree
Showing 24 changed files with 1,552 additions and 223 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ public struct UnknownHttpServiceError: HttpServiceError, Swift.Equatable {

extension UnknownHttpServiceError {


/// Creates an `UnknownHttpServiceError` from a HTTP response.
/// - Parameters:
/// - httpResponse: The `HttpResponse` for this error.
Expand Down
20 changes: 20 additions & 0 deletions Packages/ClientRuntime/Sources/Waiters/Acceptor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,26 @@ extension WaiterConfiguration {
case failure
}

/// Used as the root value of an `inputOutput` acceptor, which has the `input` and `output` fields
/// as its two top level properties.
///
/// Even though `input` and `output` are both guaranteed to be present when this type is created,
/// these properties are optional because `InputOutput` is handled like any other Smithy model object,
/// and smithy-swift currently does not support `@required` properties on Smithy models.
///
/// In the future, if smithy-swift is updated to support `@required` properties, these may be made
/// non-optional and the corresponding Smithy model's members for `input` and `output` should be
/// marked with `@required` as well.
public struct InputOutput {
public let input: Input?
public let output: Output?

public init(input: Input, output: Output) {
self.input = input
self.output = output
}
}

/// The state that the `Waiter` enters when this `Acceptor` matches the operation response.
public let state: State

Expand Down
102 changes: 102 additions & 0 deletions Packages/ClientRuntime/Sources/Waiters/JMESUtils.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

import Foundation

/// Utility functions for performing comparisons between values in JMESPath expressions.
///
/// `Bool` may be compared for equality & inequality.
///
/// `String` and a `RawRepresentable where RawValue == String` may be interchangeable compared for equality and inequality.
///
/// `Int` and `Double` may be interchangeably compared for equality, inequality, and order.
///
/// When one of the values in an order comparison is `nil`, the result is `false`.
public enum JMESUtils {

// Function for comparing Bool to Bool.

public static func compare(_ lhs: Bool?, _ comparator: (Bool?, Bool?) -> Bool, _ rhs: Bool?) -> Bool {
return comparator(lhs, rhs)
}

// Functions for comparing Double to Double.

public static func compare(_ lhs: Double?, _ comparator: (Double?, Double?) -> Bool, _ rhs: Double?) -> Bool {
comparator(lhs, rhs)
}

public static func compare(_ lhs: Double?, _ comparator: (Double, Double) -> Bool, _ rhs: Double?) -> Bool {
guard let lhs = lhs, let rhs = rhs else { return false }
return comparator(lhs, rhs)
}

// Functions for comparing Int to Int.

public static func compare(_ lhs: Int?, _ comparator: (Int?, Int?) -> Bool, _ rhs: Int?) -> Bool {
comparator(lhs, rhs)
}

public static func compare(_ lhs: Int?, _ comparator: (Int, Int) -> Bool, _ rhs: Int?) -> Bool {
guard let lhs = lhs, let rhs = rhs else { return false }
return comparator(lhs, rhs)
}

// Function for comparing String to String.

public static func compare(_ lhs: String?, _ comparator: (String?, String?) -> Bool, _ rhs: String?) -> Bool {
comparator(lhs, rhs)
}

// Function for comparing two types that are each raw representable by String.

public static func compare<L: RawRepresentable, R: RawRepresentable>(
_ lhs: L?,
_ comparator: (String?, String?) -> Bool,
_ rhs: R?
) -> Bool where L.RawValue == String, R.RawValue == String {
comparator(lhs?.rawValue, rhs?.rawValue)
}

// Extensions for comparing Int and / or Double.

public static func compare(_ lhs: Int?, _ comparator: (Double?, Double?) -> Bool, _ rhs: Double?) -> Bool {
comparator(lhs.map { Double($0) }, rhs)
}

public static func compare(_ lhs: Double?, _ comparator: (Double?, Double?) -> Bool, _ rhs: Int?) -> Bool {
comparator(lhs, rhs.map { Double($0) })
}

public static func compare(_ lhs: Int?, _ comparator: (Double, Double) -> Bool, _ rhs: Double?) -> Bool {
guard let lhs = lhs, let rhs = rhs else { return false }
return comparator(Double(lhs), rhs)
}

public static func compare(_ lhs: Double?, _ comparator: (Double, Double) -> Bool, _ rhs: Int?) -> Bool {
guard let lhs = lhs, let rhs = rhs else { return false }
return comparator(lhs, Double(rhs))
}

// Extensions for comparing String with types having raw value of String.

public static func compare<T: RawRepresentable>(
_ lhs: T?,
_ comparator: (String?, String?) -> Bool,
_ rhs: String?
) -> Bool where T.RawValue == String {
comparator(lhs?.rawValue, rhs)
}

public static func compare<T: RawRepresentable>(
_ lhs: String?,
_ comparator: (String?, String?) -> Bool,
_ rhs: T?
) -> Bool where T.RawValue == String {
comparator(lhs, rhs?.rawValue)
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ class HttpResponseGenerator(
httpOperations.forEach {
httpResponseBindingErrorGenerator.render(ctx, it)
HttpResponseBindingErrorNarrowGenerator(ctx, it, unknownServiceErrorSymbol).render()
WaiterTypedErrorGenerator(ctx, it, unknownServiceErrorSymbol).render()
}

val modeledErrors = httpOperations.flatMap { it.errors }.map { ctx.model.expectShape(it) as StructureShape }.toSet()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/

package software.amazon.smithy.swift.codegen.utils

import software.amazon.smithy.swift.codegen.SwiftWriter
import java.util.UUID

/**
* A class which uses a writer to "record" code into an in-memory buffer,
* then "plays it back" onto the writer at a later time.
*
* Useful when the result of rendering an expression is needed before the expression
* is actually to be included in the code.
*/
class BufferWriter(
val writer: SwiftWriter,
private val mutableBuffer: MutableList<String> = mutableListOf()
) {
/**
* Record whatever text is written in the block to the internal buffer.
*/
fun record(block: (SwiftWriter) -> Unit) {
// Create a random string to use as a section identifier.
val sectionID = UUID.randomUUID().toString()

// Open a new section on the writer.
writer.pushState(sectionID)

// onSection will "intercept" the code written to the given section and
// allow it to be redirected using the trailing closure.
writer.onSection(sectionID) {
val text = it as? String
if (text != null) {
// Text sometimes comes in as multiple strings, so trim indentation then split it
mutableBuffer.addAll(text.trimIndent().split("\n"))
}
}

// Perform the writes performed in the trailing closure to this function.
block(writer)

// Close the section, returning the writer to its previous state.
writer.popState()
}

/**
* Write the contents of the buffer to the writer, then erase the buffer.
*/
fun playback() {
mutableBuffer.forEach { writer.write(it) }
mutableBuffer.removeAll { true }
}
}
Loading

0 comments on commit 4dfe803

Please sign in to comment.