Skip to content

Commit

Permalink
Merge pull request #2 from amzn/duplicate_normalized_error_names
Browse files Browse the repository at this point in the history
Detect errors that will normalize to the same name
  • Loading branch information
tachyonics authored Apr 3, 2019
2 parents 0b039a6 + 438117e commit 3fa1b5a
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 39 deletions.
18 changes: 11 additions & 7 deletions Sources/ServiceModelCodeGeneration/ModelErrorsDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

import Foundation

public typealias ErrorType = (normalizedName: String, identity: String)

/**
Delegate protocol that can customize the generation of errors
from the Service Model.
Expand All @@ -39,7 +41,7 @@ public protocol ModelErrorsDelegate {
- errorTypes: The sorted list of error types.
*/
func errorTypeAdditionalImportsGenerator(fileBuilder: FileBuilder,
errorTypes: [String])
errorTypes: [ErrorType])

/**
Generator for the error type additional error identities.
Expand All @@ -49,7 +51,7 @@ public protocol ModelErrorsDelegate {
- errorTypes: The sorted list of error types.
*/
func errorTypeAdditionalErrorIdentitiesGenerator(fileBuilder: FileBuilder,
errorTypes: [String])
errorTypes: [ErrorType])

/**
Indicates the number of additional error cases that will be added.
Expand All @@ -59,7 +61,7 @@ public protocol ModelErrorsDelegate {
- errorTypes: The sorted list of error types.
*/
func errorTypeWillAddAdditionalCases(fileBuilder: FileBuilder,
errorTypes: [String]) -> Int
errorTypes: [ErrorType]) -> Int

/**
Generator for the error type additional error cases.
Expand All @@ -69,7 +71,7 @@ public protocol ModelErrorsDelegate {
- errorTypes: The sorted list of error types.
*/
func errorTypeAdditionalErrorCasesGenerator(fileBuilder: FileBuilder,
errorTypes: [String])
errorTypes: [ErrorType])

/**
Generator for the error type CodingKeys.
Expand All @@ -79,16 +81,18 @@ public protocol ModelErrorsDelegate {
- errorTypes: The sorted list of error types.
*/
func errorTypeCodingKeysGenerator(fileBuilder: FileBuilder,
errorTypes: [String])
errorTypes: [ErrorType])

/**
Generator for the error type identity.

- Parameters:
- fileBuilder: The FileBuilder to output to.
- codingErrorUnknownError: the error that can be thrown for an unknown error.
- Returns: the variable name used to store the identity.
*/
func errorTypeIdentityGenerator(fileBuilder: FileBuilder) -> String
func errorTypeIdentityGenerator(fileBuilder: FileBuilder,
codingErrorUnknownError: String) -> String

/**
Generator for the error type additional decode cases using the error identity.
Expand All @@ -98,7 +102,7 @@ public protocol ModelErrorsDelegate {
- errorTypes: The sorted list of error types.
*/
func errorTypeAdditionalErrorDecodeStatementsGenerator(fileBuilder: FileBuilder,
errorTypes: [String])
errorTypes: [ErrorType])

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import ServiceModelEntities
public extension ServiceModelCodeGenerator {

internal func generateErrorDefinition(fileBuilder: FileBuilder,
sortedErrors: [String],
sortedErrors: [ErrorType],
delegate: ModelErrorsDelegate) {
let baseName = applicationDescription.baseName
addErrorIdentities(fileBuilder: fileBuilder, sortedErrors: sortedErrors,
Expand Down Expand Up @@ -59,7 +59,9 @@ public extension ServiceModelCodeGenerator {
fileBuilder.appendLine("public init(from decoder: Decoder) throws {", postInc: true)

// add code to get the identity variable from the delegate
let identityVariable = delegate.errorTypeIdentityGenerator(fileBuilder: fileBuilder)
let identityVariable = delegate.errorTypeIdentityGenerator(
fileBuilder: fileBuilder,
codingErrorUnknownError: "\(baseName)CodingError.unknownError")

fileBuilder.appendEmptyLine()
fileBuilder.appendLine("switch \(identityVariable) {")
Expand All @@ -85,7 +87,7 @@ public extension ServiceModelCodeGenerator {
}

private func generateErrorPayloadType(fileBuilder: FileBuilder,
sortedErrors: [String],
sortedErrors: [ErrorType],
delegate: ModelErrorsDelegate) -> String {
// if there are additional errors, create a payload type for them
let errorPayloadTypeName = "\(applicationDescription.baseName)ErrorPayload"
Expand All @@ -111,19 +113,19 @@ public extension ServiceModelCodeGenerator {
return errorPayloadTypeName
}

private func addErrorCases(fileBuilder: FileBuilder, sortedErrors: [String],
private func addErrorCases(fileBuilder: FileBuilder, sortedErrors: [ErrorType],
errorPayloadTypeName: String) {
// for each of the errors
for name in sortedErrors {
for error in sortedErrors {
let enumName = getNormalizedEnumCaseName(
modelTypeName: name.normalizedErrorName,
modelTypeName: error.normalizedName,
inStructure: "\(applicationDescription.baseName)Error",
usingUpperCamelCase: true)

let payload: String
// if this is an error from the model
if model.errorTypes.contains(name) {
payload = name
if model.errorTypes.contains(error.identity) {
payload = error.identity
} else {
payload = errorPayloadTypeName
}
Expand All @@ -133,26 +135,26 @@ public extension ServiceModelCodeGenerator {
}

private func addErrorDecodeStatements(fileBuilder: FileBuilder,
sortedErrors: [String],
sortedErrors: [ErrorType],
delegate: ModelErrorsDelegate,
errorPayloadTypeName: String) {
let baseName = applicationDescription.baseName

// for each of the errors
for name in sortedErrors {
for error in sortedErrors {
let identityName = getNormalizedVariableName(
modelTypeName: name.normalizedErrorName,
modelTypeName: error.normalizedName,
inStructure: nil,
reservedWordsAllowed: true)

let parameterName = getNormalizedVariableName(
modelTypeName: name.normalizedErrorName,
modelTypeName: error.normalizedName,
inStructure: "\(baseName)Error",
reservedWordsAllowed: true)

let payload: String
if model.errorTypes.contains(name) {
payload = name
if model.errorTypes.contains(error.identity) {
payload = error.identity
} else {
payload = errorPayloadTypeName
}
Expand Down Expand Up @@ -180,7 +182,7 @@ public extension ServiceModelCodeGenerator {
}

private func addErrorIdentities(fileBuilder: FileBuilder,
sortedErrors: [String],
sortedErrors: [ErrorType],
delegate: ModelErrorsDelegate) {
if delegate.canExpectValidationError {
fileBuilder.appendLine("""
Expand All @@ -190,13 +192,14 @@ public extension ServiceModelCodeGenerator {
}

// for each of the errors
for name in sortedErrors {
for error in sortedErrors {
let identityName = getNormalizedVariableName(
modelTypeName: name.normalizedErrorName,
modelTypeName: error.normalizedName,
inStructure: nil,
reservedWordsAllowed: true)

let identity = model.errorCodeMappings[name] ?? name
let rawIdentity = error.identity
let identity = model.errorCodeMappings[rawIdentity] ?? rawIdentity

fileBuilder.appendLine("""
private let \(identityName)Identity = "\(identity)"
Expand All @@ -207,7 +210,7 @@ public extension ServiceModelCodeGenerator {
}

private func getEntityType(fileBuilder: FileBuilder,
sortedErrors: [String],
sortedErrors: [ErrorType],
delegate: ModelErrorsDelegate) -> String {
// add any additional error cases from the delegate
let additionalCases = delegate.errorTypeWillAddAdditionalCases(fileBuilder: fileBuilder, errorTypes: sortedErrors)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,7 @@ public extension ServiceModelCodeGenerator {
allErrorTypes = model.errorTypes
}

let sortedErrors = allErrorTypes.sorted { entry1, entry2 in
return entry1 < entry2
}
let sortedErrors = getSortedErrors(allErrorTypes: allErrorTypes)

delegate.errorTypeAdditionalImportsGenerator(fileBuilder: fileBuilder, errorTypes: sortedErrors)

Expand Down Expand Up @@ -85,9 +83,45 @@ public extension ServiceModelCodeGenerator {
let fileName = "\(baseName)ModelErrors.swift"
fileBuilder.write(toFile: fileName, atFilePath: "\(applicationDescription.baseFilePath)/Sources/\(baseName)Model")
}

private func getSortedErrors(allErrorTypes: Set<String>) -> [ErrorType] {
// determine if any errors will normalize to the same name
var errorNameCount: [String: Int] = [:]
allErrorTypes.forEach { errorIdentity in
let normalizedErrorName = errorIdentity.normalizedErrorName

if let existingCount = errorNameCount[normalizedErrorName] {
errorNameCount[normalizedErrorName] = existingCount + 1
} else {
errorNameCount[normalizedErrorName] = 1
}
}

let rawSortedErrors = allErrorTypes.sorted { entry1, entry2 in
return entry1 < entry2
}

let sortedErrors: [ErrorType] = rawSortedErrors.map { errorIdentity in
let normalizedErrorName = errorIdentity.normalizedErrorName

let errorNameCount = errorNameCount[normalizedErrorName] ?? 1

if errorNameCount > 1 {
// don't normalize the name as there will be a clash
return (normalizedName: errorIdentity.upperToLowerCamelCase,
identity: errorIdentity)
} else {
// use the normalized name
return (normalizedName: normalizedErrorName,
identity: errorIdentity)
}
}

return sortedErrors
}

private func generateProtocolExtension(fileBuilder: FileBuilder,
sortedErrors: [String],
sortedErrors: [ErrorType],
delegate: ModelErrorsDelegate) {
let baseName = applicationDescription.baseName
guard !sortedErrors.isEmpty else {
Expand All @@ -107,8 +141,8 @@ public extension ServiceModelCodeGenerator {
fileBuilder.incIndent()
fileBuilder.incIndent()
// for each of the errors
for name in sortedErrors {
let internalName = name.normalizedErrorName
for error in sortedErrors {
let internalName = error.normalizedName
fileBuilder.appendLine("""
case .\(internalName):
return \(internalName)Identity
Expand Down Expand Up @@ -139,8 +173,8 @@ public extension ServiceModelCodeGenerator {
fileBuilder.incIndent()
fileBuilder.incIndent()
// for each of the errors
for name in sortedErrors {
let internalName = name.normalizedErrorName
for error in sortedErrors {
let internalName = error.normalizedName
fileBuilder.appendLine("""
case .\(internalName)(let details):
try details.encode(to: encoder)
Expand All @@ -159,7 +193,7 @@ public extension ServiceModelCodeGenerator {
}

private func generateErrorOptionSet(fileBuilder: FileBuilder,
sortedErrors: [String],
sortedErrors: [ErrorType],
delegate: ModelErrorsDelegate) {
let baseName = applicationDescription.baseName

Expand All @@ -174,8 +208,8 @@ public extension ServiceModelCodeGenerator {
""")

// for each of the errors
for (index, name) in sortedErrors.enumerated() {
let internalName = name.normalizedErrorName
for (index, error) in sortedErrors.enumerated() {
let internalName = error.normalizedName
fileBuilder.appendLine(" public static let \(internalName) = \(baseName)ErrorTypes(rawValue: \(index + 1))")
}

Expand All @@ -190,8 +224,8 @@ public extension ServiceModelCodeGenerator {
fileBuilder.incIndent()
fileBuilder.incIndent()
// for each of the errors
for (index, name) in sortedErrors.enumerated() {
let internalName = name.normalizedErrorName
for (index, error) in sortedErrors.enumerated() {
let internalName = error.normalizedName
fileBuilder.appendLine("""
case \(index + 1):
return \(internalName)Identity
Expand Down

0 comments on commit 3fa1b5a

Please sign in to comment.