Skip to content

Commit

Permalink
[Refactor] Introduce TranslatorContext (2/2) (#640)
Browse files Browse the repository at this point in the history
### Motivation

Depends on #638, so
don't review until that lands.

Continuation of #638, broke up into multiple PRs for easier review.

Also a non-functional change, purely a refactoring.

### Modifications

Changed a bunch of TypeMatcher methods from static to being instance
methods, which gives them access to the translator context.

Moved a few other methods that depended on the static methods to be
extensions on the closest type that already has access to the context.

### Result

Now TypeAssigner/TypeMatcher can take runtime configuration, e.g.
through feature flags.

### Test Plan

All tests still pass.
  • Loading branch information
czechboy0 authored Oct 4, 2024
1 parent 07ee940 commit f1d7d8b
Show file tree
Hide file tree
Showing 11 changed files with 40 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ extension TypesFileTranslator {
parent: typeName
)
let associatedDeclarations: [Declaration]
if TypeMatcher.isInlinable(schema) {
if typeMatcher.isInlinable(schema) {
associatedDeclarations = try translateSchema(
typeName: propertyType.typeName,
schema: schema,
Expand All @@ -81,7 +81,7 @@ extension TypesFileTranslator {
context: context
)
var referenceStack = ReferenceStack.empty
let isKeyValuePairSchema = try TypeMatcher.isKeyValuePair(
let isKeyValuePairSchema = try typeMatcher.isKeyValuePair(
schema,
referenceStack: &referenceStack,
components: components
Expand Down Expand Up @@ -173,7 +173,7 @@ extension TypesFileTranslator {
parent: typeName
)
let associatedDeclarations: [Declaration]
if TypeMatcher.isInlinable(schema) {
if typeMatcher.isInlinable(schema) {
associatedDeclarations = try translateSchema(
typeName: childType.typeName,
schema: schema,
Expand All @@ -183,7 +183,7 @@ extension TypesFileTranslator {
associatedDeclarations = []
}
var referenceStack = ReferenceStack.empty
let isKeyValuePair = try TypeMatcher.isKeyValuePair(
let isKeyValuePair = try typeMatcher.isKeyValuePair(
schema,
referenceStack: &referenceStack,
components: components
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ extension TypesFileTranslator {
parent: typeName
)
let associatedDeclarations: [Declaration]
if TypeMatcher.isInlinable(value) {
if typeMatcher.isInlinable(value) {
associatedDeclarations = try translateSchema(
typeName: propertyType.typeName,
schema: value,
Expand Down Expand Up @@ -153,7 +153,7 @@ extension TypesFileTranslator {
components: components,
inParent: parent
)
if TypeMatcher.isInlinable(schema) {
if typeMatcher.isInlinable(schema) {
associatedDeclarations = try translateSchema(
typeName: valueTypeUsage.typeName,
schema: schema,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,11 @@ extension MultipartSchemaTypedContent {
}
}

extension SchemaContent {
extension TypeMatcher {
/// Returns a Boolean value whether the schema is a multipart content type and is referenceable.
var isReferenceableMultipart: Bool {
guard contentType.isMultipart else { return false }
let ref = TypeMatcher.multipartElementTypeReferenceIfReferenceable(schema: schema, encoding: encoding)
func isReferenceableMultipart(_ content: SchemaContent) -> Bool {
guard content.contentType.isMultipart else { return false }
let ref = multipartElementTypeReferenceIfReferenceable(schema: content.schema, encoding: content.encoding)
return ref == nil
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ extension TypesFileTranslator {
inParent: typeName.appending(swiftComponent: nil, jsonComponent: "content")
)
let associatedDeclarations: [Declaration]
if TypeMatcher.isInlinable(schema) {
if typeMatcher.isInlinable(schema) {
associatedDeclarations = try translateSchema(
typeName: bodyTypeUsage.typeName,
schema: schema,
Expand Down Expand Up @@ -117,7 +117,7 @@ extension TypesFileTranslator {
schema: JSONSchema
) throws -> [Declaration] {
let associatedDeclarations: [Declaration]
if TypeMatcher.isInlinable(schema) {
if typeMatcher.isInlinable(schema) {
associatedDeclarations = try translateSchema(typeName: typeName, schema: schema, overrides: .none)
} else {
associatedDeclarations = []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,22 +57,14 @@ extension TypedParameter {
/// The location of the parameter in the HTTP request.
var location: OpenAPI.Parameter.Context.Location { parameter.location }

/// A schema to be inlined.
///
/// - Returns: Nil when schema is referenceable.
var inlineableSchema: JSONSchema? { schema.inlineableSchema }
}

extension UnresolvedSchema {

/// A schema to be inlined.
///
/// - Returns: Nil when schema is referenceable.
var inlineableSchema: JSONSchema? {
switch self {
switch schema {
case .a: return nil
case let .b(schema):
if TypeMatcher.isInlinable(schema) { return schema }
if TypeMatcher(context: context).isInlinable(schema) { return schema }
return nil
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ extension TypesFileTranslator {
let contentTypeName = typeName.appending(jsonComponent: "content")
let contents = requestBody.contents
for content in contents {
if TypeMatcher.isInlinable(content.content.schema) || content.content.isReferenceableMultipart {
if typeMatcher.isInlinable(content.content.schema) || typeMatcher.isReferenceableMultipart(content.content)
{
let inlineTypeDecls = try translateRequestBodyContentInTypes(content)
bodyMembers.append(contentsOf: inlineTypeDecls)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ extension TypesFileTranslator {
let associatedType = typedContent.resolvedTypeUsage
let content = typedContent.content
let schema = content.schema
if TypeMatcher.isInlinable(schema) || content.isReferenceableMultipart {
if typeMatcher.isInlinable(schema) || typeMatcher.isReferenceableMultipart(content) {
let decls: [Declaration]
if contentType.isMultipart {
decls = try translateMultipartBody(typedContent)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ extension TypesFileTranslator {
let schema = header.schema
let typeUsage = header.typeUsage
let associatedDeclarations: [Declaration]
if TypeMatcher.isInlinable(schema) {
if typeMatcher.isInlinable(schema) {
associatedDeclarations = try translateSchema(typeName: typeUsage.typeName, schema: schema, overrides: .none)
} else {
associatedDeclarations = []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,9 @@ struct TypeAssigner {
inParent parent: TypeName
) throws -> TypeUsage {
let multipartBodyElementTypeName: TypeName
if let ref = TypeMatcher.multipartElementTypeReferenceIfReferenceable(schema: schema, encoding: encoding) {
if let ref = TypeMatcher(context: context)
.multipartElementTypeReferenceIfReferenceable(schema: schema, encoding: encoding)
{
multipartBodyElementTypeName = try typeName(for: ref)
} else {
let swiftSafeName = context.asSwiftSafeName(hint)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ struct TypeMatcher {
func tryMatchBuiltinType(for schema: JSONSchema.Schema) -> TypeUsage? {
Self._tryMatchRecursive(
for: schema,
test: { schema in Self._tryMatchBuiltinNonRecursive(for: schema) },
test: { schema in _tryMatchBuiltinNonRecursive(for: schema) },
matchedArrayHandler: { elementType, nullableItems in
nullableItems ? elementType.asOptional.asArray : elementType.asArray
},
Expand All @@ -68,7 +68,7 @@ struct TypeMatcher {
try Self._tryMatchRecursive(
for: schema.value,
test: { (schema) -> TypeUsage? in
if let builtinType = Self._tryMatchBuiltinNonRecursive(for: schema) { return builtinType }
if let builtinType = _tryMatchBuiltinNonRecursive(for: schema) { return builtinType }
guard case let .reference(ref, _) = schema else { return nil }
return try TypeAssigner(context: context).typeName(for: ref).asUsage
},
Expand All @@ -88,9 +88,9 @@ struct TypeMatcher {
/// - A reference
/// - Parameter schema: The schema to match a referenceable type for.
/// - Returns: `true` if the schema is referenceable; `false` otherwise.
static func isReferenceable(_ schema: JSONSchema) -> Bool {
func isReferenceable(_ schema: JSONSchema) -> Bool {
// This logic should be kept in sync with `tryMatchReferenceableType`.
_tryMatchRecursive(
Self._tryMatchRecursive(
for: schema.value,
test: { schema in
if _tryMatchBuiltinNonRecursive(for: schema) != nil { return true }
Expand All @@ -110,7 +110,7 @@ struct TypeMatcher {
/// - A reference
/// - Parameter schema: The schema to match a referenceable type for.
/// - Returns: `true` if the schema is referenceable; `false` otherwise.
static func isReferenceable(_ schema: UnresolvedSchema?) -> Bool {
func isReferenceable(_ schema: UnresolvedSchema?) -> Bool {
guard let schema else {
// fragment type is referenceable
return true
Expand All @@ -132,7 +132,7 @@ struct TypeMatcher {
/// referenceable.
/// - Parameter schema: The schema to match a referenceable type for.
/// - Returns: `true` if the schema is inlinable; `false` otherwise.
static func isInlinable(_ schema: JSONSchema) -> Bool { !isReferenceable(schema) }
func isInlinable(_ schema: JSONSchema) -> Bool { !isReferenceable(schema) }

/// Returns a Boolean value that indicates whether the schema
/// needs to be defined inline.
Expand All @@ -143,14 +143,14 @@ struct TypeMatcher {
/// referenceable.
/// - Parameter schema: The schema to match a referenceable type for.
/// - Returns: `true` if the schema is inlinable; `false` otherwise.
static func isInlinable(_ schema: UnresolvedSchema?) -> Bool { !isReferenceable(schema) }
func isInlinable(_ schema: UnresolvedSchema?) -> Bool { !isReferenceable(schema) }

/// Return a reference to a multipart element type if the provided schema is referenceable.
/// - Parameters:
/// - schema: The schema to try to reference.
/// - encoding: The associated encoding.
/// - Returns: A reference if the schema is referenceable, nil otherwise.
static func multipartElementTypeReferenceIfReferenceable(
func multipartElementTypeReferenceIfReferenceable(
schema: UnresolvedSchema?,
encoding: OrderedDictionary<String, OpenAPI.Content.Encoding>?
) -> OpenAPI.Reference<JSONSchema>? {
Expand All @@ -174,11 +174,9 @@ struct TypeMatcher {
/// - components: The reusable components from the OpenAPI document.
/// - Throws: An error if there's an issue while checking the schema.
/// - Returns: `true` if the schema is a key-value pair; `false` otherwise.
static func isKeyValuePair(
_ schema: JSONSchema,
referenceStack: inout ReferenceStack,
components: OpenAPI.Components
) throws -> Bool {
func isKeyValuePair(_ schema: JSONSchema, referenceStack: inout ReferenceStack, components: OpenAPI.Components)
throws -> Bool
{
switch schema.value {
case .object, .fragment: return true
case .null, .boolean, .number, .integer, .string, .array, .not: return false
Expand Down Expand Up @@ -222,7 +220,7 @@ struct TypeMatcher {
/// - components: The reusable components from the OpenAPI document.
/// - Throws: An error if there's an issue while checking the schema.
/// - Returns: `true` if the schema is a key-value pair; `false` otherwise.
static func isKeyValuePair(
func isKeyValuePair(
_ schema: UnresolvedSchema?,
referenceStack: inout ReferenceStack,
components: OpenAPI.Components
Expand Down Expand Up @@ -285,7 +283,7 @@ struct TypeMatcher {
/// - Parameter schema: The schema to match a referenceable type for.
/// - Returns: A type usage for the schema if the schema is built-in.
/// Otherwise, returns nil.
private static func _tryMatchBuiltinNonRecursive(for schema: JSONSchema.Schema) -> TypeUsage? {
private func _tryMatchBuiltinNonRecursive(for schema: JSONSchema.Schema) -> TypeUsage? {
let typeName: TypeName
switch schema {
case .boolean(_): typeName = .swift("Bool")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,8 @@ final class Test_TypeMatcher: Test_Core {
.fullyQualifiedSwiftName,
name
)
XCTAssertTrue(TypeMatcher.isReferenceable(schema))
XCTAssertFalse(TypeMatcher.isInlinable(schema))
XCTAssertTrue(typeMatcher.isReferenceable(schema))
XCTAssertFalse(typeMatcher.isInlinable(schema))
}
}

Expand All @@ -145,8 +145,8 @@ final class Test_TypeMatcher: Test_Core {
typeMatcher.tryMatchBuiltinType(for: schema.value),
"Type is expected to not match a builtin type: \(schema)"
)
XCTAssertFalse(TypeMatcher.isReferenceable(schema), "Expected schema not to be referenceable: \(schema)")
XCTAssertTrue(TypeMatcher.isInlinable(schema), "Expected schema to be inlinable: \(schema)")
XCTAssertFalse(typeMatcher.isReferenceable(schema), "Expected schema not to be referenceable: \(schema)")
XCTAssertTrue(typeMatcher.isInlinable(schema), "Expected schema to be inlinable: \(schema)")
}
}

Expand Down Expand Up @@ -175,7 +175,7 @@ final class Test_TypeMatcher: Test_Core {
for schema in Self.keyValuePairTypes {
var referenceStack = ReferenceStack.empty
XCTAssertTrue(
try TypeMatcher.isKeyValuePair(schema, referenceStack: &referenceStack, components: components),
try typeMatcher.isKeyValuePair(schema, referenceStack: &referenceStack, components: components),
"Type is expected to be a key-value pair schema: \(schema)"
)
}
Expand Down Expand Up @@ -249,13 +249,12 @@ final class Test_TypeMatcher: Test_Core {
]
func testMultipartElementTypeReferenceIfReferenceableTypes() throws {
for (schema, encoding, name) in Self.multipartElementTypeReferenceIfReferenceableTypes {
let actualName = TypeMatcher.multipartElementTypeReferenceIfReferenceable(
let actualName = typeMatcher.multipartElementTypeReferenceIfReferenceable(
schema: schema,
encoding: encoding
)?
.name
XCTAssertEqual(actualName, name)
}
}

}

0 comments on commit f1d7d8b

Please sign in to comment.