From 31cf1d59178d831b073d18b820ad0745b4184641 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Sun, 2 Jul 2023 05:16:59 +0330 Subject: [PATCH 001/100] try generator plugin --- Package.swift | 14 ++ Plugins/OpenAPIGenerator/plugin.swift | 2 +- .../plugin.swift | 139 ++++++++++++++++++ .../GenerateCommand.swift | 8 +- .../GenerateOptions+runGenerator.swift | 10 +- .../InvocationKind.swift | 17 +++ .../swift-openapi-generator/UserConfig.swift | 2 + .../runGenerator.swift | 4 +- 8 files changed, 187 insertions(+), 9 deletions(-) create mode 100644 Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift create mode 100644 Sources/swift-openapi-generator/InvocationKind.swift diff --git a/Package.swift b/Package.swift index bebbf51a..02e91407 100644 --- a/Package.swift +++ b/Package.swift @@ -134,5 +134,19 @@ let package = Package( "swift-openapi-generator", ] ), + + .plugin( + name: "OpenAPIGeneratorCommandPlugin", + capability: Target.PluginCapability.command( + intent: .custom( + verb: "generate-openapi-code", + description: "Generates OpenAPI code" + ), + permissions: [.writeToPackageDirectory(reason: "To generate OpenAPI code")] + ), + dependencies: [ + "swift-openapi-generator", + ] + ) ] ) diff --git a/Plugins/OpenAPIGenerator/plugin.swift b/Plugins/OpenAPIGenerator/plugin.swift index 33a52d41..d700f385 100644 --- a/Plugins/OpenAPIGenerator/plugin.swift +++ b/Plugins/OpenAPIGenerator/plugin.swift @@ -85,7 +85,7 @@ struct SwiftOpenAPIGeneratorPlugin { "generate", "\(doc)", "--config", "\(config)", "--output-directory", "\(genSourcesDir)", - "--is-plugin-invocation", + "--invocation-kind", "BuildTool" ], environment: [:], inputFiles: [ diff --git a/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift b/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift new file mode 100644 index 00000000..ee913c88 --- /dev/null +++ b/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift @@ -0,0 +1,139 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftOpenAPIGenerator open source project +// +// Copyright (c) 2023 Apple Inc. and the SwiftOpenAPIGenerator project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftOpenAPIGenerator project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// +import PackagePlugin +import Foundation + +@main +struct SwiftOpenAPIGeneratorPlugin { + enum Error: Swift.Error, CustomStringConvertible, LocalizedError { + case incompatibleTarget(targetName: String) + case multiTargetFound + case noConfigFound(targetName: String) + case noDocumentFound(targetName: String) + case multiConfigFound(targetName: String, files: [Path]) + case multiDocumentFound(targetName: String, files: [Path]) + + var description: String { + switch self { + case .incompatibleTarget(let targetName): + return + "Incompatible target called '\(targetName)'. Only Swift source targets can be used with the Swift OpenAPI generator plugin." + case .multiTargetFound: + return "Please choose a specific target for the OpenAPI Generator Command plugin. This Command plugin can't run on multiple targets at the same time." + case .noConfigFound(let targetName): + return + "No config file found in the target named '\(targetName)'. Add a file called 'openapi-generator-config.yaml' or 'openapi-generator-config.yml' to the target's source directory. See documentation for details." + case .noDocumentFound(let targetName): + return + "No OpenAPI document found in the target named '\(targetName)'. Add a file called 'openapi.yaml', 'openapi.yml' or 'openapi.json' (can also be a symlink) to the target's source directory. See documentation for details." + case .multiConfigFound(let targetName, let files): + return + "Multiple config files found in the target named '\(targetName)', but exactly one is required. Found \(files.map(\.description).joined(separator: " "))." + case .multiDocumentFound(let targetName, let files): + return + "Multiple OpenAPI documents found in the target named '\(targetName)', but exactly one is required. Found \(files.map(\.description).joined(separator: " "))." + } + } + + var errorDescription: String? { + description + } + } + + private var supportedConfigFiles: Set { Set(["yaml", "yml"].map { "openapi-generator-config." + $0 }) } + private var supportedDocFiles: Set { Set(["yaml", "yml", "json"].map { "openapi." + $0 }) } + + func runCommand( + pluginWorkDirectory: PackagePlugin.Path, + tool: (String) throws -> PackagePlugin.PluginContext.Tool, + sourceFiles: FileList, + targetName: String + ) throws { + let inputFiles = sourceFiles + let matchedConfigs = inputFiles.filter { supportedConfigFiles.contains($0.path.lastComponent) }.map(\.path) + guard matchedConfigs.count > 0 else { + throw Error.noConfigFound(targetName: targetName) + } + guard matchedConfigs.count == 1 else { + throw Error.multiConfigFound(targetName: targetName, files: matchedConfigs) + } + let config = matchedConfigs[0] + + let matchedDocs = inputFiles.filter { supportedDocFiles.contains($0.path.lastComponent) }.map(\.path) + guard matchedDocs.count > 0 else { + throw Error.noDocumentFound(targetName: targetName) + } + guard matchedDocs.count == 1 else { + throw Error.multiDocumentFound(targetName: targetName, files: matchedDocs) + } + let doc = matchedDocs[0] + let genSourcesDir = pluginWorkDirectory.appending("GeneratedSources") +// let outputFiles: [Path] = GeneratorMode.allCases.map { genSourcesDir.appending($0.outputFileName) } + + let tool = try tool("swift-openapi-generator") + let toolUrl = URL(fileURLWithPath: tool.path.string) + let process = Process() + process.executableURL = toolUrl + process.arguments = [ + "generate", "\(doc)", + "--config", "\(config)", + "--output-directory", "\(genSourcesDir)", + "--invocation-kind", "Command" + ] + try process.run() + } +} + +extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { + func performCommand( + context: PluginContext, + arguments: [String] + ) async throws { + guard context.package.targets.count == 1 else { + throw Error.multiTargetFound + } + let target = context.package.targets[0] + guard let swiftTarget = target as? SwiftSourceModuleTarget else { + throw Error.incompatibleTarget(targetName: target.name) + } + return try runCommand( + pluginWorkDirectory: context.pluginWorkDirectory, + tool: context.tool, + sourceFiles: swiftTarget.sourceFiles, + targetName: target.name + ) + } +} + +#if canImport(XcodeProjectPlugin) +import XcodeProjectPlugin + +extension SwiftOpenAPIGeneratorPlugin: XcodeCommandPlugin { + func performCommand( + context: XcodePluginContext, + arguments: [String] + ) throws { + guard context.xcodeProject.targets.count == 1 else { + throw Error.multiTargetFound + } + let target = context.xcodeProject.targets[0] + return try runCommand( + pluginWorkDirectory: context.pluginWorkDirectory, + tool: context.tool, + sourceFiles: target.inputFiles, + targetName: target.displayName + ) + } +} +#endif diff --git a/Sources/swift-openapi-generator/GenerateCommand.swift b/Sources/swift-openapi-generator/GenerateCommand.swift index fc1ac08f..34d3a07d 100644 --- a/Sources/swift-openapi-generator/GenerateCommand.swift +++ b/Sources/swift-openapi-generator/GenerateCommand.swift @@ -42,16 +42,18 @@ struct _GenerateCommand: AsyncParsableCommand { ) var outputDirectory: URL = URL(fileURLWithPath: FileManager.default.currentDirectoryPath) - @Flag( + @Option( help: "Whether this invocation is from the SwiftPM plugin. We always need to produce all files when invoked from the plugin. Non-requested modes produce empty files." ) - var isPluginInvocation: Bool = false + var invocationKind: InvocationKind = .CLI func run() async throws { try generate.runGenerator( outputDirectory: outputDirectory, - isPluginInvocation: isPluginInvocation + invocationKind: invocationKind ) } } + +extension InvocationKind: ExpressibleByArgument { } diff --git a/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift b/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift index 02627486..ced9ac3b 100644 --- a/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift +++ b/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift @@ -29,9 +29,13 @@ extension _GenerateOptions { /// a limitation of the build system used by SwiftPM under the hood. func runGenerator( outputDirectory: URL, - isPluginInvocation: Bool + invocationKind: InvocationKind ) throws { let config = try loadedConfig() + if invocationKind == .BuildTool && config?.disabledAsBuildToolPlugin == true { + print("Plugin disabled for BuildTools") + return + } let sortedModes = try resolvedModes(config) let resolvedAdditionalImports = resolvedAdditionalImports(config) let configs: [Config] = sortedModes.map { @@ -62,7 +66,7 @@ extension _GenerateOptions { - Output directory: \(outputDirectory.path) - Diagnostics output path: \(diagnosticsOutputPath?.path ?? "") - Current directory: \(FileManager.default.currentDirectoryPath) - - Is plugin invocation: \(isPluginInvocation) + - Invocation kind: \(invocationKind.rawValue) - Additional imports: \(resolvedAdditionalImports.isEmpty ? "" : resolvedAdditionalImports.joined(separator: ", ")) """ ) @@ -70,7 +74,7 @@ extension _GenerateOptions { try _Tool.runGenerator( doc: doc, configs: configs, - isPluginInvocation: isPluginInvocation, + invocationKind: invocationKind, outputDirectory: outputDirectory, diagnostics: diagnostics ) diff --git a/Sources/swift-openapi-generator/InvocationKind.swift b/Sources/swift-openapi-generator/InvocationKind.swift new file mode 100644 index 00000000..2ea9dc79 --- /dev/null +++ b/Sources/swift-openapi-generator/InvocationKind.swift @@ -0,0 +1,17 @@ + +public enum InvocationKind: String { + case CLI + case BuildTool + case Command + + var isPluginInvocation: Bool { + switch self { + case .CLI: + return false + case .BuildTool: + return true + case .Command: + return true + } + } +} diff --git a/Sources/swift-openapi-generator/UserConfig.swift b/Sources/swift-openapi-generator/UserConfig.swift index e2267680..c2998ccc 100644 --- a/Sources/swift-openapi-generator/UserConfig.swift +++ b/Sources/swift-openapi-generator/UserConfig.swift @@ -26,4 +26,6 @@ struct _UserConfig: Codable { /// A list of names of additional imports that are added to every /// generated Swift file. var additionalImports: [String]? + + var disabledAsBuildToolPlugin: Bool? } diff --git a/Sources/swift-openapi-generator/runGenerator.swift b/Sources/swift-openapi-generator/runGenerator.swift index 1300f04c..466d01db 100644 --- a/Sources/swift-openapi-generator/runGenerator.swift +++ b/Sources/swift-openapi-generator/runGenerator.swift @@ -29,7 +29,7 @@ extension _Tool { static func runGenerator( doc: URL, configs: [Config], - isPluginInvocation: Bool, + invocationKind: InvocationKind, outputDirectory: URL, diagnostics: DiagnosticCollector ) throws { @@ -51,7 +51,7 @@ extension _Tool { diagnostics: diagnostics ) } - if isPluginInvocation { + if invocationKind.isPluginInvocation { let nonGeneratedModes = Set(GeneratorMode.allCases).subtracting(configs.map(\.mode)) for mode in nonGeneratedModes.sorted() { let path = filePathForMode(mode) From c5a404dafa9682ee2f0fd4cec488ebeab80c7f56 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Sun, 2 Jul 2023 05:20:27 +0330 Subject: [PATCH 002/100] add new plugin to products --- Package.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Package.swift b/Package.swift index 02e91407..bb8a0bc4 100644 --- a/Package.swift +++ b/Package.swift @@ -27,6 +27,7 @@ let package = Package( products: [ .executable(name: "swift-openapi-generator", targets: ["swift-openapi-generator"]), .plugin(name: "OpenAPIGenerator", targets: ["OpenAPIGenerator"]), + .plugin(name: "OpenAPIGeneratorCommandPlugin", targets: ["OpenAPIGeneratorCommandPlugin"]), .library(name: "_OpenAPIGeneratorCore", targets: ["_OpenAPIGeneratorCore"]), ], dependencies: [ From cfb128d89f2c49a7f9b351c4bfd15609e87060ca Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Sun, 2 Jul 2023 05:32:47 +0330 Subject: [PATCH 003/100] better debugging --- .../OpenAPIGeneratorCommandPlugin/plugin.swift | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift b/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift index ee913c88..2ddf257d 100644 --- a/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift @@ -18,7 +18,7 @@ import Foundation struct SwiftOpenAPIGeneratorPlugin { enum Error: Swift.Error, CustomStringConvertible, LocalizedError { case incompatibleTarget(targetName: String) - case multiTargetFound + case multiTargetFound(targetNames: [String]) case noConfigFound(targetName: String) case noDocumentFound(targetName: String) case multiConfigFound(targetName: String, files: [Path]) @@ -29,8 +29,8 @@ struct SwiftOpenAPIGeneratorPlugin { case .incompatibleTarget(let targetName): return "Incompatible target called '\(targetName)'. Only Swift source targets can be used with the Swift OpenAPI generator plugin." - case .multiTargetFound: - return "Please choose a specific target for the OpenAPI Generator Command plugin. This Command plugin can't run on multiple targets at the same time." + case .multiTargetFound(let targetNames): + return "Please choose a specific target for the OpenAPI Generator Command plugin. This Command plugin can't run on multiple targets at the same time. The current target names are: \(targetNames)" case .noConfigFound(let targetName): return "No config file found in the target named '\(targetName)'. Add a file called 'openapi-generator-config.yaml' or 'openapi-generator-config.yml' to the target's source directory. See documentation for details." @@ -79,7 +79,6 @@ struct SwiftOpenAPIGeneratorPlugin { } let doc = matchedDocs[0] let genSourcesDir = pluginWorkDirectory.appending("GeneratedSources") -// let outputFiles: [Path] = GeneratorMode.allCases.map { genSourcesDir.appending($0.outputFileName) } let tool = try tool("swift-openapi-generator") let toolUrl = URL(fileURLWithPath: tool.path.string) @@ -101,10 +100,14 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { arguments: [String] ) async throws { guard context.package.targets.count == 1 else { - throw Error.multiTargetFound + print("Error with context:", context) + print("Args:", arguments) + throw Error.multiTargetFound(targetNames: context.package.targets.map(\.name)) } let target = context.package.targets[0] guard let swiftTarget = target as? SwiftSourceModuleTarget else { + print("Error with context:", context) + print("Args:", arguments) throw Error.incompatibleTarget(targetName: target.name) } return try runCommand( @@ -125,7 +128,9 @@ extension SwiftOpenAPIGeneratorPlugin: XcodeCommandPlugin { arguments: [String] ) throws { guard context.xcodeProject.targets.count == 1 else { - throw Error.multiTargetFound + throw Error.multiTargetFound( + targetNames: context.xcodeProject.targets.map(\.displayName) + ) } let target = context.xcodeProject.targets[0] return try runCommand( From b6e4ffe642a8e8d1d1ecefee39c8f132aae592e0 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Sun, 2 Jul 2023 05:42:43 +0330 Subject: [PATCH 004/100] Update plugin.swift --- Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift b/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift index 2ddf257d..8bf4e326 100644 --- a/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift @@ -100,13 +100,13 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { arguments: [String] ) async throws { guard context.package.targets.count == 1 else { - print("Error with context:", context) + print("PWD:", context.pluginWorkDirectory) print("Args:", arguments) throw Error.multiTargetFound(targetNames: context.package.targets.map(\.name)) } let target = context.package.targets[0] guard let swiftTarget = target as? SwiftSourceModuleTarget else { - print("Error with context:", context) + print("PWD:", context.pluginWorkDirectory) print("Args:", arguments) throw Error.incompatibleTarget(targetName: target.name) } @@ -128,6 +128,8 @@ extension SwiftOpenAPIGeneratorPlugin: XcodeCommandPlugin { arguments: [String] ) throws { guard context.xcodeProject.targets.count == 1 else { + print("PWD:", context.pluginWorkDirectory) + print("Args:", arguments) throw Error.multiTargetFound( targetNames: context.xcodeProject.targets.map(\.displayName) ) From 7fbde61088bc8aea1c12da42806a42ecb7f6e86a Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Sun, 2 Jul 2023 06:06:01 +0330 Subject: [PATCH 005/100] fixes --- .../plugin.swift | 45 ++++++++++++------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift b/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift index 8bf4e326..e307ab16 100644 --- a/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift @@ -18,7 +18,10 @@ import Foundation struct SwiftOpenAPIGeneratorPlugin { enum Error: Swift.Error, CustomStringConvertible, LocalizedError { case incompatibleTarget(targetName: String) - case multiTargetFound(targetNames: [String]) + case badArguments(arguments: [String]) + // The description is only suitable for Xcode, as it's only thrown in Xcode plugins. + case noTargetsMatchingTargetName(targetName: String) + case tooManyTargetsMatchingTargetName(targetNames: [String]) case noConfigFound(targetName: String) case noDocumentFound(targetName: String) case multiConfigFound(targetName: String, files: [Path]) @@ -29,8 +32,12 @@ struct SwiftOpenAPIGeneratorPlugin { case .incompatibleTarget(let targetName): return "Incompatible target called '\(targetName)'. Only Swift source targets can be used with the Swift OpenAPI generator plugin." - case .multiTargetFound(let targetNames): - return "Please choose a specific target for the OpenAPI Generator Command plugin. This Command plugin can't run on multiple targets at the same time. The current target names are: \(targetNames)" + case .badArguments(let arguments): + return "Bad arguments provided: \(arguments). On Xcode, use Xcode's run plugin UI to choose a specific target. On CLI, pass a specific target's name to the command like so: '--target TARGET_NAME'" + case .noTargetsMatchingTargetName(let targetName): + return "No target called '\(targetName)' were found. Use Xcode's UI to choose a single specific target before triggering the command plugin." + case .tooManyTargetsMatchingTargetName(let targetNames): + return "Too many targets found matching the provided target name: '\(targetNames)'. Target name must be specific enough for the plugin to only find a single target." case .noConfigFound(let targetName): return "No config file found in the target named '\(targetName)'. Add a file called 'openapi-generator-config.yaml' or 'openapi-generator-config.yml' to the target's source directory. See documentation for details." @@ -99,15 +106,18 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { context: PluginContext, arguments: [String] ) async throws { - guard context.package.targets.count == 1 else { - print("PWD:", context.pluginWorkDirectory) - print("Args:", arguments) - throw Error.multiTargetFound(targetNames: context.package.targets.map(\.name)) + guard arguments.count == 2, arguments[0] == "--target" else { + throw Error.badArguments(arguments: arguments) } - let target = context.package.targets[0] + let targetName = arguments[1] + let matchingTargets = try context.package.targets(named: [targetName]) + // `matchingTargets.count` can't be 0 because + // `targets(named:)` would throw an error for that. + guard matchingTargets.count == 1 else { + throw Error.tooManyTargetsMatchingTargetName(targetNames: matchingTargets.map(\.name)) + } + let target = matchingTargets[0] guard let swiftTarget = target as? SwiftSourceModuleTarget else { - print("PWD:", context.pluginWorkDirectory) - print("Args:", arguments) throw Error.incompatibleTarget(targetName: target.name) } return try runCommand( @@ -127,14 +137,15 @@ extension SwiftOpenAPIGeneratorPlugin: XcodeCommandPlugin { context: XcodePluginContext, arguments: [String] ) throws { - guard context.xcodeProject.targets.count == 1 else { - print("PWD:", context.pluginWorkDirectory) - print("Args:", arguments) - throw Error.multiTargetFound( - targetNames: context.xcodeProject.targets.map(\.displayName) - ) + guard arguments.count == 2, arguments[0] == "--target" else { + throw Error.badArguments(arguments: arguments) + } + let targetName = arguments[1] + guard let target = context.xcodeProject.targets.first(where: { + $0.displayName == targetName + }) else { + throw Error.noTargetsMatchingTargetName(targetName: targetName) } - let target = context.xcodeProject.targets[0] return try runCommand( pluginWorkDirectory: context.pluginWorkDirectory, tool: context.tool, From d65e0bfa0fe83ea879d37c80b8f49e548bf0e24a Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Sun, 2 Jul 2023 06:24:17 +0330 Subject: [PATCH 006/100] correct working dir --- .../OpenAPIGeneratorCommandPlugin/plugin.swift | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift b/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift index e307ab16..f54b6b5d 100644 --- a/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift @@ -21,6 +21,7 @@ struct SwiftOpenAPIGeneratorPlugin { case badArguments(arguments: [String]) // The description is only suitable for Xcode, as it's only thrown in Xcode plugins. case noTargetsMatchingTargetName(targetName: String) + // The description is not suitable for Xcode, as it's not thrown in Xcode plugins. case tooManyTargetsMatchingTargetName(targetNames: [String]) case noConfigFound(targetName: String) case noDocumentFound(targetName: String) @@ -62,7 +63,7 @@ struct SwiftOpenAPIGeneratorPlugin { private var supportedDocFiles: Set { Set(["yaml", "yml", "json"].map { "openapi." + $0 }) } func runCommand( - pluginWorkDirectory: PackagePlugin.Path, + targetWorkingDirectory: PackagePlugin.Path, tool: (String) throws -> PackagePlugin.PluginContext.Tool, sourceFiles: FileList, targetName: String @@ -85,7 +86,7 @@ struct SwiftOpenAPIGeneratorPlugin { throw Error.multiDocumentFound(targetName: targetName, files: matchedDocs) } let doc = matchedDocs[0] - let genSourcesDir = pluginWorkDirectory.appending("GeneratedSources") + let genSourcesDir = targetWorkingDirectory.appending("GeneratedSources") let tool = try tool("swift-openapi-generator") let toolUrl = URL(fileURLWithPath: tool.path.string) @@ -121,7 +122,7 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { throw Error.incompatibleTarget(targetName: target.name) } return try runCommand( - pluginWorkDirectory: context.pluginWorkDirectory, + targetWorkingDirectory: target.directory, tool: context.tool, sourceFiles: swiftTarget.sourceFiles, targetName: target.name @@ -141,16 +142,19 @@ extension SwiftOpenAPIGeneratorPlugin: XcodeCommandPlugin { throw Error.badArguments(arguments: arguments) } let targetName = arguments[1] - guard let target = context.xcodeProject.targets.first(where: { + guard let xcodeTarget = context.xcodeProject.targets.first(where: { $0.displayName == targetName }) else { throw Error.noTargetsMatchingTargetName(targetName: targetName) } + guard let target = xcodeTarget as? SourceModuleTarget else { + throw Error.incompatibleTarget(targetName: targetName) + } return try runCommand( - pluginWorkDirectory: context.pluginWorkDirectory, + targetWorkingDirectory: target.directory, tool: context.tool, - sourceFiles: target.inputFiles, - targetName: target.displayName + sourceFiles: xcodeTarget.inputFiles, + targetName: xcodeTarget.displayName ) } } From 45c88248013de7f7238569657321c64d8509fdc0 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Sun, 2 Jul 2023 06:36:57 +0330 Subject: [PATCH 007/100] correct file-manipulation logic --- Sources/swift-openapi-generator/runGenerator.swift | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Sources/swift-openapi-generator/runGenerator.swift b/Sources/swift-openapi-generator/runGenerator.swift index 466d01db..d67c7f33 100644 --- a/Sources/swift-openapi-generator/runGenerator.swift +++ b/Sources/swift-openapi-generator/runGenerator.swift @@ -96,16 +96,16 @@ extension _Tool { @discardableResult static func replaceFileContents(at path: URL, with contents: () throws -> Data) throws -> Bool { let data = try contents() - let didChange: Bool if FileManager.default.fileExists(atPath: path.path) { let existingData = try? Data(contentsOf: path) - didChange = existingData != data + if existingData == data { + return false + } else { + try data.write(to: path) + return true + } } else { - didChange = true - } - if didChange { - try data.write(to: path) + return FileManager.default.createFile(atPath: path.path, contents: data) } - return didChange } } From 133a3c14ac0b06fcce66ae4561040e2815c18bb7 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Sun, 2 Jul 2023 06:46:59 +0330 Subject: [PATCH 008/100] better file manipulation --- .../runGenerator.swift | 44 ++++++++++++++----- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/Sources/swift-openapi-generator/runGenerator.swift b/Sources/swift-openapi-generator/runGenerator.swift index d67c7f33..24071c12 100644 --- a/Sources/swift-openapi-generator/runGenerator.swift +++ b/Sources/swift-openapi-generator/runGenerator.swift @@ -39,23 +39,24 @@ extension _Tool { } catch { throw ValidationError("Failed to load the OpenAPI document at path \(doc.path), error: \(error)") } - let filePathForMode: (GeneratorMode) -> URL = { mode in - outputDirectory.appendingPathComponent(mode.outputFileName) - } for config in configs { try runGenerator( doc: doc, docData: docData, config: config, - outputFilePath: filePathForMode(config.mode), + outputDirectory: outputDirectory, + outputFileName: config.mode.outputFileName, diagnostics: diagnostics ) } if invocationKind.isPluginInvocation { let nonGeneratedModes = Set(GeneratorMode.allCases).subtracting(configs.map(\.mode)) for mode in nonGeneratedModes.sorted() { - let path = filePathForMode(mode) - try replaceFileContents(at: path, with: { Data() }) + try replaceFileContents( + inDirectory: outputDirectory, + fileName: mode.outputFileName, + with: { Data() } + ) } } } @@ -72,10 +73,14 @@ extension _Tool { doc: URL, docData: Data, config: Config, - outputFilePath: URL, + outputDirectory: URL, + outputFileName: String, diagnostics: DiagnosticCollector ) throws { - let didChange = try replaceFileContents(at: outputFilePath) { + let didChange = try replaceFileContents( + inDirectory: outputDirectory, + fileName: outputFileName + ) { let output = try _OpenAPIGeneratorCore.runGenerator( input: .init(absolutePath: doc, contents: docData), config: config, @@ -83,7 +88,7 @@ extension _Tool { ) return output.contents } - print("File \(outputFilePath.lastPathComponent): \(didChange ? "changed" : "unchanged")") + print("File with name '\(outputFileName)' in directory '\(outputDirectory.path)': \(didChange ? "changed" : "unchanged")") } /// Evaluates a closure to generate file data and writes the data to disk @@ -94,9 +99,24 @@ extension _Tool { /// - Throws: When writing to disk fails. /// - Returns: `true` if the generated contents changed, otherwise `false`. @discardableResult - static func replaceFileContents(at path: URL, with contents: () throws -> Data) throws -> Bool { + static func replaceFileContents( + inDirectory outputDirectory: URL, + fileName: String, + with contents: () throws -> Data + ) throws -> Bool { + let fm = FileManager.default + + // Create directory if doesn't exist + if !fm.fileExists(atPath: outputDirectory.path) { + try fm.createDirectory( + at: outputDirectory, + withIntermediateDirectories: true + ) + } + + let path = outputDirectory.appendingPathComponent(fileName) let data = try contents() - if FileManager.default.fileExists(atPath: path.path) { + if fm.fileExists(atPath: path.path) { let existingData = try? Data(contentsOf: path) if existingData == data { return false @@ -105,7 +125,7 @@ extension _Tool { return true } } else { - return FileManager.default.createFile(atPath: path.path, contents: data) + return fm.createFile(atPath: path.path, contents: data) } } } From 12fec26c91717ec9550a18473e75ac1ab7f5516d Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Sun, 2 Jul 2023 06:48:52 +0330 Subject: [PATCH 009/100] generate empty files only for buildtool plugins --- Sources/swift-openapi-generator/runGenerator.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/swift-openapi-generator/runGenerator.swift b/Sources/swift-openapi-generator/runGenerator.swift index 24071c12..07553dad 100644 --- a/Sources/swift-openapi-generator/runGenerator.swift +++ b/Sources/swift-openapi-generator/runGenerator.swift @@ -49,7 +49,7 @@ extension _Tool { diagnostics: diagnostics ) } - if invocationKind.isPluginInvocation { + if invocationKind == .BuildTool { let nonGeneratedModes = Set(GeneratorMode.allCases).subtracting(configs.map(\.mode)) for mode in nonGeneratedModes.sorted() { try replaceFileContents( From db96114a440ab50574ecd5a10b193cbe9c86ed6e Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Sun, 2 Jul 2023 06:53:00 +0330 Subject: [PATCH 010/100] change directory name --- Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift b/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift index f54b6b5d..f0bda5e6 100644 --- a/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift @@ -86,7 +86,9 @@ struct SwiftOpenAPIGeneratorPlugin { throw Error.multiDocumentFound(targetName: targetName, files: matchedDocs) } let doc = matchedDocs[0] - let genSourcesDir = targetWorkingDirectory.appending("GeneratedSources") + // Can't use `GeneratedSources` because the BuildTool plugin uses that + // And Xcode might throw "Filename used twice" errors. + let genSourcesDir = targetWorkingDirectory.appending("OpenAPIGeneratedSources") let tool = try tool("swift-openapi-generator") let toolUrl = URL(fileURLWithPath: tool.path.string) From 7fd39a23c92622dc06b545756af4681de78a05f4 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Sun, 2 Jul 2023 07:04:30 +0330 Subject: [PATCH 011/100] perform cleanup for previous builds --- .../GenerateOptions+runGenerator.swift | 3 ++- Sources/swift-openapi-generator/runGenerator.swift | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift b/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift index ced9ac3b..4f30d36e 100644 --- a/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift +++ b/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift @@ -33,7 +33,8 @@ extension _GenerateOptions { ) throws { let config = try loadedConfig() if invocationKind == .BuildTool && config?.disabledAsBuildToolPlugin == true { - print("Plugin disabled for BuildTools") + print("Plugin disabled for BuildTools. Will clean up files if there are any leftovers from previous builds.") + try _Tool.runCleanup(outputDirectory: outputDirectory) return } let sortedModes = try resolvedModes(config) diff --git a/Sources/swift-openapi-generator/runGenerator.swift b/Sources/swift-openapi-generator/runGenerator.swift index 07553dad..b43e6c2b 100644 --- a/Sources/swift-openapi-generator/runGenerator.swift +++ b/Sources/swift-openapi-generator/runGenerator.swift @@ -128,4 +128,11 @@ extension _Tool { return fm.createFile(atPath: path.path, contents: data) } } + + static func runCleanup(outputDirectory: URL) throws { + let fm = FileManager.default + if fm.fileExists(atPath: outputDirectory.path) { + try fm.removeItem(at: outputDirectory) + } + } } From fbd69d1325f22537e6b40c9551b9248d82af3b4d Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Sun, 2 Jul 2023 07:08:21 +0330 Subject: [PATCH 012/100] better cleanup --- Sources/swift-openapi-generator/runGenerator.swift | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Sources/swift-openapi-generator/runGenerator.swift b/Sources/swift-openapi-generator/runGenerator.swift index b43e6c2b..d76f4a32 100644 --- a/Sources/swift-openapi-generator/runGenerator.swift +++ b/Sources/swift-openapi-generator/runGenerator.swift @@ -130,9 +130,12 @@ extension _Tool { } static func runCleanup(outputDirectory: URL) throws { - let fm = FileManager.default - if fm.fileExists(atPath: outputDirectory.path) { - try fm.removeItem(at: outputDirectory) + for mode in GeneratorMode.allCases { + try replaceFileContents( + inDirectory: outputDirectory, + fileName: mode.outputFileName, + with: { Data() } + ) } } } From d6005dca0e6b218279d02545b644db0ff54886b9 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Sun, 2 Jul 2023 07:10:24 +0330 Subject: [PATCH 013/100] refinements --- .../GenerateOptions+runGenerator.swift | 2 +- Sources/swift-openapi-generator/runGenerator.swift | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift b/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift index 4f30d36e..10fffc5a 100644 --- a/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift +++ b/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift @@ -34,7 +34,7 @@ extension _GenerateOptions { let config = try loadedConfig() if invocationKind == .BuildTool && config?.disabledAsBuildToolPlugin == true { print("Plugin disabled for BuildTools. Will clean up files if there are any leftovers from previous builds.") - try _Tool.runCleanup(outputDirectory: outputDirectory) + try _Tool.runBuildToolCleanup(outputDirectory: outputDirectory) return } let sortedModes = try resolvedModes(config) diff --git a/Sources/swift-openapi-generator/runGenerator.swift b/Sources/swift-openapi-generator/runGenerator.swift index d76f4a32..cb06b6f2 100644 --- a/Sources/swift-openapi-generator/runGenerator.swift +++ b/Sources/swift-openapi-generator/runGenerator.swift @@ -49,6 +49,9 @@ extension _Tool { diagnostics: diagnostics ) } + + // Swift expects us to always create these files in BuildTool plugins, + // so we create the unused files, but empty. if invocationKind == .BuildTool { let nonGeneratedModes = Set(GeneratorMode.allCases).subtracting(configs.map(\.mode)) for mode in nonGeneratedModes.sorted() { @@ -129,8 +132,9 @@ extension _Tool { } } - static func runCleanup(outputDirectory: URL) throws { + static func runBuildToolCleanup(outputDirectory: URL) throws { for mode in GeneratorMode.allCases { + // Swift expects us to always create these files, so we create them but empty. try replaceFileContents( inDirectory: outputDirectory, fileName: mode.outputFileName, From 02cda112408c322433e24407e9062559ac711c4d Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Sun, 2 Jul 2023 07:21:54 +0330 Subject: [PATCH 014/100] Update plugin.swift --- Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift b/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift index f0bda5e6..1a07785a 100644 --- a/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift @@ -88,7 +88,7 @@ struct SwiftOpenAPIGeneratorPlugin { let doc = matchedDocs[0] // Can't use `GeneratedSources` because the BuildTool plugin uses that // And Xcode might throw "Filename used twice" errors. - let genSourcesDir = targetWorkingDirectory.appending("OpenAPIGeneratedSources") + let genSourcesDir = targetWorkingDirectory.appending("GeneratedSources") let tool = try tool("swift-openapi-generator") let toolUrl = URL(fileURLWithPath: tool.path.string) From 7f7b33a7873581f20b2ba63fc996b88520d33d58 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Sun, 2 Jul 2023 07:30:31 +0330 Subject: [PATCH 015/100] fixes for same-name file creations by plugins --- Plugins/OpenAPIGenerator/plugin.swift | 2 +- Sources/_OpenAPIGeneratorCore/GeneratorMode.swift | 6 +++--- .../ClientTranslator/ClientTranslator.swift | 2 +- .../ServerTranslator/ServerTranslator.swift | 2 +- .../TypesTranslator/TypesFileTranslator.swift | 2 +- Sources/swift-openapi-generator/GenerateCommand.swift | 2 +- .../GenerateOptions+runGenerator.swift | 2 +- Sources/swift-openapi-generator/runGenerator.swift | 11 ++++++++--- 8 files changed, 17 insertions(+), 12 deletions(-) diff --git a/Plugins/OpenAPIGenerator/plugin.swift b/Plugins/OpenAPIGenerator/plugin.swift index d700f385..cd014dd2 100644 --- a/Plugins/OpenAPIGenerator/plugin.swift +++ b/Plugins/OpenAPIGenerator/plugin.swift @@ -76,7 +76,7 @@ struct SwiftOpenAPIGeneratorPlugin { } let doc = matchedDocs[0] let genSourcesDir = pluginWorkDirectory.appending("GeneratedSources") - let outputFiles: [Path] = GeneratorMode.allCases.map { genSourcesDir.appending($0.outputFileName) } + let outputFiles: [Path] = GeneratorMode.allCases.map { genSourcesDir.appending($0.outputFileNameSuffix) } return [ .buildCommand( displayName: "Running swift-openapi-generator", diff --git a/Sources/_OpenAPIGeneratorCore/GeneratorMode.swift b/Sources/_OpenAPIGeneratorCore/GeneratorMode.swift index ff9b0259..ff59dd4c 100644 --- a/Sources/_OpenAPIGeneratorCore/GeneratorMode.swift +++ b/Sources/_OpenAPIGeneratorCore/GeneratorMode.swift @@ -37,7 +37,7 @@ public enum GeneratorMode: String, Codable, CaseIterable { extension GeneratorMode { /// The Swift file name including its file extension. - public var outputFileName: String { + public var outputFileNameSuffix: String { switch self { case .types: return "Types.swift" @@ -49,8 +49,8 @@ extension GeneratorMode { } /// The Swift file names for all supported generator mode values. - public static var allOutputFileNames: [String] { - GeneratorMode.allCases.map(\.outputFileName) + public static var allOutputFileNameSuffixes: [String] { + GeneratorMode.allCases.map(\.outputFileNameSuffix) } /// Defines an order in which generators should be run. diff --git a/Sources/_OpenAPIGeneratorCore/Translator/ClientTranslator/ClientTranslator.swift b/Sources/_OpenAPIGeneratorCore/Translator/ClientTranslator/ClientTranslator.swift index 49abd18b..c741af32 100644 --- a/Sources/_OpenAPIGeneratorCore/Translator/ClientTranslator/ClientTranslator.swift +++ b/Sources/_OpenAPIGeneratorCore/Translator/ClientTranslator/ClientTranslator.swift @@ -148,7 +148,7 @@ struct ClientFileTranslator: FileTranslator { return StructuredSwiftRepresentation( file: .init( - name: GeneratorMode.client.outputFileName, + name: GeneratorMode.client.outputFileNameSuffix, contents: .init( topComment: topComment, imports: imports, diff --git a/Sources/_OpenAPIGeneratorCore/Translator/ServerTranslator/ServerTranslator.swift b/Sources/_OpenAPIGeneratorCore/Translator/ServerTranslator/ServerTranslator.swift index f1360371..3576e044 100644 --- a/Sources/_OpenAPIGeneratorCore/Translator/ServerTranslator/ServerTranslator.swift +++ b/Sources/_OpenAPIGeneratorCore/Translator/ServerTranslator/ServerTranslator.swift @@ -128,7 +128,7 @@ struct ServerFileTranslator: FileTranslator { return StructuredSwiftRepresentation( file: .init( - name: GeneratorMode.server.outputFileName, + name: GeneratorMode.server.outputFileNameSuffix, contents: .init( topComment: topComment, imports: imports, diff --git a/Sources/_OpenAPIGeneratorCore/Translator/TypesTranslator/TypesFileTranslator.swift b/Sources/_OpenAPIGeneratorCore/Translator/TypesTranslator/TypesFileTranslator.swift index 703115b1..c5faaaa9 100644 --- a/Sources/_OpenAPIGeneratorCore/Translator/TypesTranslator/TypesFileTranslator.swift +++ b/Sources/_OpenAPIGeneratorCore/Translator/TypesTranslator/TypesFileTranslator.swift @@ -65,7 +65,7 @@ struct TypesFileTranslator: FileTranslator { return StructuredSwiftRepresentation( file: .init( - name: GeneratorMode.types.outputFileName, + name: GeneratorMode.types.outputFileNameSuffix, contents: typesFile ) ) diff --git a/Sources/swift-openapi-generator/GenerateCommand.swift b/Sources/swift-openapi-generator/GenerateCommand.swift index 34d3a07d..7935ba27 100644 --- a/Sources/swift-openapi-generator/GenerateCommand.swift +++ b/Sources/swift-openapi-generator/GenerateCommand.swift @@ -38,7 +38,7 @@ struct _GenerateCommand: AsyncParsableCommand { @Option( help: - "Output directory where the generated files are written. Warning: Replaces any existing files with the same filename. Reserved filenames: \(GeneratorMode.allOutputFileNames.joined(separator: ", "))" + "Output directory where the generated files are written. Warning: Replaces any existing files with the same filename. Reserved filename suffixes: \(GeneratorMode.allOutputFileNameSuffixes.joined(separator: ", "))" ) var outputDirectory: URL = URL(fileURLWithPath: FileManager.default.currentDirectoryPath) diff --git a/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift b/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift index 10fffc5a..68678533 100644 --- a/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift +++ b/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift @@ -63,7 +63,7 @@ extension _GenerateOptions { - OpenAPI document path: \(doc.path) - Configuration path: \(self.config?.path ?? "") - Generator modes: \(sortedModes.map(\.rawValue).joined(separator: ", ")) - - Output file names: \(sortedModes.map(\.outputFileName).joined(separator: ", ")) + - Output file name suffixes: \(sortedModes.map(\.outputFileNameSuffix).joined(separator: ", ")) - Output directory: \(outputDirectory.path) - Diagnostics output path: \(diagnosticsOutputPath?.path ?? "") - Current directory: \(FileManager.default.currentDirectoryPath) diff --git a/Sources/swift-openapi-generator/runGenerator.swift b/Sources/swift-openapi-generator/runGenerator.swift index cb06b6f2..2c8df3e3 100644 --- a/Sources/swift-openapi-generator/runGenerator.swift +++ b/Sources/swift-openapi-generator/runGenerator.swift @@ -40,12 +40,17 @@ extension _Tool { throw ValidationError("Failed to load the OpenAPI document at path \(doc.path), error: \(error)") } for config in configs { + // BuildTool plugins guarantee the compiler that they will create some files + // with specific names (the same as `config.mode.outputFileNameSuffix`, for us) + // To make sure the BuildTool plugin and the Command plugin don't create files with + // the same names, we add a prefix to the names. + let outputFileNamePrefix = invocationKind == .BuildTool ? "" : "Generated_" try runGenerator( doc: doc, docData: docData, config: config, outputDirectory: outputDirectory, - outputFileName: config.mode.outputFileName, + outputFileName: outputFileNamePrefix + config.mode.outputFileNameSuffix, diagnostics: diagnostics ) } @@ -57,7 +62,7 @@ extension _Tool { for mode in nonGeneratedModes.sorted() { try replaceFileContents( inDirectory: outputDirectory, - fileName: mode.outputFileName, + fileName: mode.outputFileNameSuffix, with: { Data() } ) } @@ -137,7 +142,7 @@ extension _Tool { // Swift expects us to always create these files, so we create them but empty. try replaceFileContents( inDirectory: outputDirectory, - fileName: mode.outputFileName, + fileName: mode.outputFileNameSuffix, with: { Data() } ) } From 11ee90479bd8ec68d2895d3aef40b4938c9c5549 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Sun, 2 Jul 2023 10:42:46 +0330 Subject: [PATCH 016/100] better configuration --- .../GenerateOptions+runGenerator.swift | 26 ++++++++++++++++--- .../InvocationKind.swift | 17 +++--------- .../swift-openapi-generator/UserConfig.swift | 7 ++++- .../runGenerator.swift | 18 +++++++++---- 4 files changed, 44 insertions(+), 24 deletions(-) diff --git a/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift b/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift index 68678533..61db4554 100644 --- a/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift +++ b/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift @@ -32,11 +32,29 @@ extension _GenerateOptions { invocationKind: InvocationKind ) throws { let config = try loadedConfig() - if invocationKind == .BuildTool && config?.disabledAsBuildToolPlugin == true { - print("Plugin disabled for BuildTools. Will clean up files if there are any leftovers from previous builds.") - try _Tool.runBuildToolCleanup(outputDirectory: outputDirectory) - return + + switch invocationKind { + case .BuildToolPlugin: + guard (config?.pluginMode ?? .BuildTool) == .BuildTool else { + print("Plugin disabled for BuildTool plugins. Will clean up files if there are any leftovers from previous builds.") + try _Tool.runCleanup( + outputDirectory: outputDirectory, + forInvocationKind: invocationKind + ) + return + } + case .CommandPlugin: + guard (config?.pluginMode ?? .BuildTool) == .Command else { + print("Plugin disabled for Command plugins. Will clean up files if there are any leftovers from previous builds.") + try _Tool.runCleanup( + outputDirectory: outputDirectory, + forInvocationKind: invocationKind + ) + return + } + default: break } + let sortedModes = try resolvedModes(config) let resolvedAdditionalImports = resolvedAdditionalImports(config) let configs: [Config] = sortedModes.map { diff --git a/Sources/swift-openapi-generator/InvocationKind.swift b/Sources/swift-openapi-generator/InvocationKind.swift index 2ea9dc79..f0350b51 100644 --- a/Sources/swift-openapi-generator/InvocationKind.swift +++ b/Sources/swift-openapi-generator/InvocationKind.swift @@ -1,17 +1,6 @@ -public enum InvocationKind: String { +public enum InvocationKind: String, Codable { + case BuildToolPlugin + case CommandPlugin case CLI - case BuildTool - case Command - - var isPluginInvocation: Bool { - switch self { - case .CLI: - return false - case .BuildTool: - return true - case .Command: - return true - } - } } diff --git a/Sources/swift-openapi-generator/UserConfig.swift b/Sources/swift-openapi-generator/UserConfig.swift index c2998ccc..94c0845e 100644 --- a/Sources/swift-openapi-generator/UserConfig.swift +++ b/Sources/swift-openapi-generator/UserConfig.swift @@ -20,6 +20,11 @@ import _OpenAPIGeneratorCore /// Client.swift in one invocation of the command-line tool. struct _UserConfig: Codable { + enum PluginMode: String, Codable { + case BuildTool + case Command + } + /// A list of modes to use, in other words, which Swift files to generate. var generate: [GeneratorMode] @@ -27,5 +32,5 @@ struct _UserConfig: Codable { /// generated Swift file. var additionalImports: [String]? - var disabledAsBuildToolPlugin: Bool? + var pluginMode: PluginMode? } diff --git a/Sources/swift-openapi-generator/runGenerator.swift b/Sources/swift-openapi-generator/runGenerator.swift index 2c8df3e3..e1418b21 100644 --- a/Sources/swift-openapi-generator/runGenerator.swift +++ b/Sources/swift-openapi-generator/runGenerator.swift @@ -44,20 +44,19 @@ extension _Tool { // with specific names (the same as `config.mode.outputFileNameSuffix`, for us) // To make sure the BuildTool plugin and the Command plugin don't create files with // the same names, we add a prefix to the names. - let outputFileNamePrefix = invocationKind == .BuildTool ? "" : "Generated_" try runGenerator( doc: doc, docData: docData, config: config, outputDirectory: outputDirectory, - outputFileName: outputFileNamePrefix + config.mode.outputFileNameSuffix, + outputFileName: fullFileName(config.mode, invocationKind: invocationKind), diagnostics: diagnostics ) } // Swift expects us to always create these files in BuildTool plugins, // so we create the unused files, but empty. - if invocationKind == .BuildTool { + if invocationKind == .BuildToolPlugin { let nonGeneratedModes = Set(GeneratorMode.allCases).subtracting(configs.map(\.mode)) for mode in nonGeneratedModes.sorted() { try replaceFileContents( @@ -137,12 +136,21 @@ extension _Tool { } } - static func runBuildToolCleanup(outputDirectory: URL) throws { + static func fullFileName(_ mode: GeneratorMode, invocationKind: InvocationKind) -> String { + let outputFileNamePrefix = invocationKind == .BuildToolPlugin ? "" : "Generated_" + return outputFileNamePrefix + mode.outputFileNameSuffix + } + + static func runCleanup( + outputDirectory: URL, + forInvocationKind invocationKind: InvocationKind + ) throws { for mode in GeneratorMode.allCases { + let fileName = fullFileName(mode, invocationKind: invocationKind) // Swift expects us to always create these files, so we create them but empty. try replaceFileContents( inDirectory: outputDirectory, - fileName: mode.outputFileNameSuffix, + fileName: fileName, with: { Data() } ) } From 0198e6c7b9aa057d8effd0ae5ca52fcf739ae983 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Sun, 2 Jul 2023 10:45:45 +0330 Subject: [PATCH 017/100] fix arguments --- Plugins/OpenAPIGenerator/plugin.swift | 2 +- Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Plugins/OpenAPIGenerator/plugin.swift b/Plugins/OpenAPIGenerator/plugin.swift index cd014dd2..952e9036 100644 --- a/Plugins/OpenAPIGenerator/plugin.swift +++ b/Plugins/OpenAPIGenerator/plugin.swift @@ -85,7 +85,7 @@ struct SwiftOpenAPIGeneratorPlugin { "generate", "\(doc)", "--config", "\(config)", "--output-directory", "\(genSourcesDir)", - "--invocation-kind", "BuildTool" + "--invocation-kind", "BuildToolPlugin" ], environment: [:], inputFiles: [ diff --git a/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift b/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift index 1a07785a..f4e7f777 100644 --- a/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift @@ -98,7 +98,7 @@ struct SwiftOpenAPIGeneratorPlugin { "generate", "\(doc)", "--config", "\(config)", "--output-directory", "\(genSourcesDir)", - "--invocation-kind", "Command" + "--invocation-kind", "CommandPlugin" ] try process.run() } From 7ada532ec90056d3d95ec0cfe83e576469536ae4 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Sun, 2 Jul 2023 10:53:52 +0330 Subject: [PATCH 018/100] better cleanups --- Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift | 2 -- .../GenerateOptions+runGenerator.swift | 10 ++-------- Sources/swift-openapi-generator/runGenerator.swift | 14 +++++++++----- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift b/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift index f4e7f777..142a80be 100644 --- a/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift @@ -86,8 +86,6 @@ struct SwiftOpenAPIGeneratorPlugin { throw Error.multiDocumentFound(targetName: targetName, files: matchedDocs) } let doc = matchedDocs[0] - // Can't use `GeneratedSources` because the BuildTool plugin uses that - // And Xcode might throw "Filename used twice" errors. let genSourcesDir = targetWorkingDirectory.appending("GeneratedSources") let tool = try tool("swift-openapi-generator") diff --git a/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift b/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift index 61db4554..606cffac 100644 --- a/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift +++ b/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift @@ -37,19 +37,13 @@ extension _GenerateOptions { case .BuildToolPlugin: guard (config?.pluginMode ?? .BuildTool) == .BuildTool else { print("Plugin disabled for BuildTool plugins. Will clean up files if there are any leftovers from previous builds.") - try _Tool.runCleanup( - outputDirectory: outputDirectory, - forInvocationKind: invocationKind - ) + try _Tool.runBuildToolPluginCleanup(outputDirectory: outputDirectory) return } case .CommandPlugin: guard (config?.pluginMode ?? .BuildTool) == .Command else { print("Plugin disabled for Command plugins. Will clean up files if there are any leftovers from previous builds.") - try _Tool.runCleanup( - outputDirectory: outputDirectory, - forInvocationKind: invocationKind - ) + try _Tool.runCommandPluginCleanup(outputDirectory: outputDirectory) return } default: break diff --git a/Sources/swift-openapi-generator/runGenerator.swift b/Sources/swift-openapi-generator/runGenerator.swift index e1418b21..2404f079 100644 --- a/Sources/swift-openapi-generator/runGenerator.swift +++ b/Sources/swift-openapi-generator/runGenerator.swift @@ -141,12 +141,9 @@ extension _Tool { return outputFileNamePrefix + mode.outputFileNameSuffix } - static func runCleanup( - outputDirectory: URL, - forInvocationKind invocationKind: InvocationKind - ) throws { + static func runBuildToolPluginCleanup(outputDirectory: URL) throws { for mode in GeneratorMode.allCases { - let fileName = fullFileName(mode, invocationKind: invocationKind) + let fileName = fullFileName(mode, invocationKind: .BuildToolPlugin) // Swift expects us to always create these files, so we create them but empty. try replaceFileContents( inDirectory: outputDirectory, @@ -155,4 +152,11 @@ extension _Tool { ) } } + + static func runCommandPluginCleanup(outputDirectory: URL) throws { + let fm = FileManager.default + if fm.fileExists(atPath: outputDirectory.path) { + try fm.removeItem(at: outputDirectory) + } + } } From 54601884780695b11200f474e6e31fb23a36c680 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Sun, 2 Jul 2023 11:15:35 +0330 Subject: [PATCH 019/100] better cleanups --- .../runGenerator.swift | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/Sources/swift-openapi-generator/runGenerator.swift b/Sources/swift-openapi-generator/runGenerator.swift index 2404f079..38bd8d96 100644 --- a/Sources/swift-openapi-generator/runGenerator.swift +++ b/Sources/swift-openapi-generator/runGenerator.swift @@ -155,7 +155,25 @@ extension _Tool { static func runCommandPluginCleanup(outputDirectory: URL) throws { let fm = FileManager.default - if fm.fileExists(atPath: outputDirectory.path) { + guard fm.fileExists(atPath: outputDirectory.path) else { + return + } + + // Remove each file + for mode in GeneratorMode.allCases { + let fileName = fullFileName(mode, invocationKind: .CommandPlugin) + let path = outputDirectory.appendingPathComponent(fileName) + if fm.fileExists(atPath: path.path) { + try fm.removeItem(at: path) + } + } + + // If the output directory is empty, remove it. + let outputDirectoryContents = try? fm.contentsOfDirectory( + at: outputDirectory, + includingPropertiesForKeys: nil + ) + if (outputDirectoryContents ?? []).isEmpty { try fm.removeItem(at: outputDirectory) } } From 3cb098d6e2b6472b5af4de9e96e00ed6947b6e0d Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Mon, 3 Jul 2023 00:13:54 +0330 Subject: [PATCH 020/100] move plugins shared logic to shared files --- Plugins/OpenAPIGenerator/PluginError.swift | 1 + Plugins/OpenAPIGenerator/PluginUtils.swift | 1 + Plugins/OpenAPIGenerator/plugin.swift | 77 +++---------- .../PluginError.swift | 1 + .../PluginUtils.swift | 1 + .../plugin.swift | 103 ++++-------------- Plugins/PluginsShared/PluginError.swift | 45 ++++++++ Plugins/PluginsShared/PluginUtils.swift | 59 ++++++++++ 8 files changed, 141 insertions(+), 147 deletions(-) create mode 120000 Plugins/OpenAPIGenerator/PluginError.swift create mode 120000 Plugins/OpenAPIGenerator/PluginUtils.swift create mode 120000 Plugins/OpenAPIGeneratorCommandPlugin/PluginError.swift create mode 120000 Plugins/OpenAPIGeneratorCommandPlugin/PluginUtils.swift create mode 100644 Plugins/PluginsShared/PluginError.swift create mode 100644 Plugins/PluginsShared/PluginUtils.swift diff --git a/Plugins/OpenAPIGenerator/PluginError.swift b/Plugins/OpenAPIGenerator/PluginError.swift new file mode 120000 index 00000000..2e97a77b --- /dev/null +++ b/Plugins/OpenAPIGenerator/PluginError.swift @@ -0,0 +1 @@ +../../Plugins/PluginsShared/PluginError.swift \ No newline at end of file diff --git a/Plugins/OpenAPIGenerator/PluginUtils.swift b/Plugins/OpenAPIGenerator/PluginUtils.swift new file mode 120000 index 00000000..2d3faf71 --- /dev/null +++ b/Plugins/OpenAPIGenerator/PluginUtils.swift @@ -0,0 +1 @@ +../../Plugins/PluginsShared/PluginUtils.swift \ No newline at end of file diff --git a/Plugins/OpenAPIGenerator/plugin.swift b/Plugins/OpenAPIGenerator/plugin.swift index 952e9036..bad05ff1 100644 --- a/Plugins/OpenAPIGenerator/plugin.swift +++ b/Plugins/OpenAPIGenerator/plugin.swift @@ -16,81 +16,32 @@ import Foundation @main struct SwiftOpenAPIGeneratorPlugin { - enum Error: Swift.Error, CustomStringConvertible, LocalizedError { - case incompatibleTarget(targetName: String) - case noConfigFound(targetName: String) - case noDocumentFound(targetName: String) - case multiConfigFound(targetName: String, files: [Path]) - case multiDocumentFound(targetName: String, files: [Path]) - - var description: String { - switch self { - case .incompatibleTarget(let targetName): - return - "Incompatible target called '\(targetName)'. Only Swift source targets can be used with the Swift OpenAPI generator plugin." - case .noConfigFound(let targetName): - return - "No config file found in the target named '\(targetName)'. Add a file called 'openapi-generator-config.yaml' or 'openapi-generator-config.yml' to the target's source directory. See documentation for details." - case .noDocumentFound(let targetName): - return - "No OpenAPI document found in the target named '\(targetName)'. Add a file called 'openapi.yaml', 'openapi.yml' or 'openapi.json' (can also be a symlink) to the target's source directory. See documentation for details." - case .multiConfigFound(let targetName, let files): - return - "Multiple config files found in the target named '\(targetName)', but exactly one is required. Found \(files.map(\.description).joined(separator: " "))." - case .multiDocumentFound(let targetName, let files): - return - "Multiple OpenAPI documents found in the target named '\(targetName)', but exactly one is required. Found \(files.map(\.description).joined(separator: " "))." - } - } - - var errorDescription: String? { - description - } - } - private var supportedConfigFiles: Set { Set(["yaml", "yml"].map { "openapi-generator-config." + $0 }) } private var supportedDocFiles: Set { Set(["yaml", "yml", "json"].map { "openapi." + $0 }) } func createBuildCommands( - pluginWorkDirectory: PackagePlugin.Path, - tool: (String) throws -> PackagePlugin.PluginContext.Tool, + pluginWorkDirectory: Path, + tool: (String) throws -> PluginContext.Tool, sourceFiles: FileList, targetName: String ) throws -> [Command] { - let inputFiles = sourceFiles - let matchedConfigs = inputFiles.filter { supportedConfigFiles.contains($0.path.lastComponent) }.map(\.path) - guard matchedConfigs.count > 0 else { - throw Error.noConfigFound(targetName: targetName) - } - guard matchedConfigs.count == 1 else { - throw Error.multiConfigFound(targetName: targetName, files: matchedConfigs) - } - let config = matchedConfigs[0] + let inputs = try PluginUtils.validateInputs( + workingDirectory: pluginWorkDirectory, + tool: tool, + sourceFiles: sourceFiles, + targetName: targetName + ) - let matchedDocs = inputFiles.filter { supportedDocFiles.contains($0.path.lastComponent) }.map(\.path) - guard matchedDocs.count > 0 else { - throw Error.noDocumentFound(targetName: targetName) - } - guard matchedDocs.count == 1 else { - throw Error.multiDocumentFound(targetName: targetName, files: matchedDocs) - } - let doc = matchedDocs[0] - let genSourcesDir = pluginWorkDirectory.appending("GeneratedSources") - let outputFiles: [Path] = GeneratorMode.allCases.map { genSourcesDir.appending($0.outputFileNameSuffix) } + let outputFiles: [Path] = GeneratorMode.allCases.map { inputs.genSourcesDir.appending($0.outputFileNameSuffix) } return [ .buildCommand( displayName: "Running swift-openapi-generator", - executable: try tool("swift-openapi-generator").path, - arguments: [ - "generate", "\(doc)", - "--config", "\(config)", - "--output-directory", "\(genSourcesDir)", - "--invocation-kind", "BuildToolPlugin" - ], + executable: inputs.tool.path, + arguments: inputs.arguments, environment: [:], inputFiles: [ - config, - doc, + inputs.config, + inputs.doc, ], outputFiles: outputFiles ) @@ -104,7 +55,7 @@ extension SwiftOpenAPIGeneratorPlugin: BuildToolPlugin { target: Target ) async throws -> [Command] { guard let swiftTarget = target as? SwiftSourceModuleTarget else { - throw Error.incompatibleTarget(targetName: target.name) + throw PluginError.incompatibleTarget(targetName: target.name) } return try createBuildCommands( pluginWorkDirectory: context.pluginWorkDirectory, diff --git a/Plugins/OpenAPIGeneratorCommandPlugin/PluginError.swift b/Plugins/OpenAPIGeneratorCommandPlugin/PluginError.swift new file mode 120000 index 00000000..2e97a77b --- /dev/null +++ b/Plugins/OpenAPIGeneratorCommandPlugin/PluginError.swift @@ -0,0 +1 @@ +../../Plugins/PluginsShared/PluginError.swift \ No newline at end of file diff --git a/Plugins/OpenAPIGeneratorCommandPlugin/PluginUtils.swift b/Plugins/OpenAPIGeneratorCommandPlugin/PluginUtils.swift new file mode 120000 index 00000000..2d3faf71 --- /dev/null +++ b/Plugins/OpenAPIGeneratorCommandPlugin/PluginUtils.swift @@ -0,0 +1 @@ +../../Plugins/PluginsShared/PluginUtils.swift \ No newline at end of file diff --git a/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift b/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift index 142a80be..979e8fe1 100644 --- a/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift @@ -16,88 +16,23 @@ import Foundation @main struct SwiftOpenAPIGeneratorPlugin { - enum Error: Swift.Error, CustomStringConvertible, LocalizedError { - case incompatibleTarget(targetName: String) - case badArguments(arguments: [String]) - // The description is only suitable for Xcode, as it's only thrown in Xcode plugins. - case noTargetsMatchingTargetName(targetName: String) - // The description is not suitable for Xcode, as it's not thrown in Xcode plugins. - case tooManyTargetsMatchingTargetName(targetNames: [String]) - case noConfigFound(targetName: String) - case noDocumentFound(targetName: String) - case multiConfigFound(targetName: String, files: [Path]) - case multiDocumentFound(targetName: String, files: [Path]) - - var description: String { - switch self { - case .incompatibleTarget(let targetName): - return - "Incompatible target called '\(targetName)'. Only Swift source targets can be used with the Swift OpenAPI generator plugin." - case .badArguments(let arguments): - return "Bad arguments provided: \(arguments). On Xcode, use Xcode's run plugin UI to choose a specific target. On CLI, pass a specific target's name to the command like so: '--target TARGET_NAME'" - case .noTargetsMatchingTargetName(let targetName): - return "No target called '\(targetName)' were found. Use Xcode's UI to choose a single specific target before triggering the command plugin." - case .tooManyTargetsMatchingTargetName(let targetNames): - return "Too many targets found matching the provided target name: '\(targetNames)'. Target name must be specific enough for the plugin to only find a single target." - case .noConfigFound(let targetName): - return - "No config file found in the target named '\(targetName)'. Add a file called 'openapi-generator-config.yaml' or 'openapi-generator-config.yml' to the target's source directory. See documentation for details." - case .noDocumentFound(let targetName): - return - "No OpenAPI document found in the target named '\(targetName)'. Add a file called 'openapi.yaml', 'openapi.yml' or 'openapi.json' (can also be a symlink) to the target's source directory. See documentation for details." - case .multiConfigFound(let targetName, let files): - return - "Multiple config files found in the target named '\(targetName)', but exactly one is required. Found \(files.map(\.description).joined(separator: " "))." - case .multiDocumentFound(let targetName, let files): - return - "Multiple OpenAPI documents found in the target named '\(targetName)', but exactly one is required. Found \(files.map(\.description).joined(separator: " "))." - } - } - - var errorDescription: String? { - description - } - } - - private var supportedConfigFiles: Set { Set(["yaml", "yml"].map { "openapi-generator-config." + $0 }) } - private var supportedDocFiles: Set { Set(["yaml", "yml", "json"].map { "openapi." + $0 }) } - func runCommand( - targetWorkingDirectory: PackagePlugin.Path, - tool: (String) throws -> PackagePlugin.PluginContext.Tool, + targetWorkingDirectory: Path, + tool: (String) throws -> PluginContext.Tool, sourceFiles: FileList, targetName: String ) throws { - let inputFiles = sourceFiles - let matchedConfigs = inputFiles.filter { supportedConfigFiles.contains($0.path.lastComponent) }.map(\.path) - guard matchedConfigs.count > 0 else { - throw Error.noConfigFound(targetName: targetName) - } - guard matchedConfigs.count == 1 else { - throw Error.multiConfigFound(targetName: targetName, files: matchedConfigs) - } - let config = matchedConfigs[0] - - let matchedDocs = inputFiles.filter { supportedDocFiles.contains($0.path.lastComponent) }.map(\.path) - guard matchedDocs.count > 0 else { - throw Error.noDocumentFound(targetName: targetName) - } - guard matchedDocs.count == 1 else { - throw Error.multiDocumentFound(targetName: targetName, files: matchedDocs) - } - let doc = matchedDocs[0] - let genSourcesDir = targetWorkingDirectory.appending("GeneratedSources") + let inputs = try PluginUtils.validateInputs( + workingDirectory: targetWorkingDirectory, + tool: tool, + sourceFiles: sourceFiles, + targetName: targetName + ) - let tool = try tool("swift-openapi-generator") - let toolUrl = URL(fileURLWithPath: tool.path.string) + let toolUrl = URL(fileURLWithPath: inputs.tool.path.string) let process = Process() process.executableURL = toolUrl - process.arguments = [ - "generate", "\(doc)", - "--config", "\(config)", - "--output-directory", "\(genSourcesDir)", - "--invocation-kind", "CommandPlugin" - ] + process.arguments = inputs.arguments try process.run() } } @@ -108,18 +43,18 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { arguments: [String] ) async throws { guard arguments.count == 2, arguments[0] == "--target" else { - throw Error.badArguments(arguments: arguments) + throw PluginError.badArguments(arguments: arguments) } let targetName = arguments[1] let matchingTargets = try context.package.targets(named: [targetName]) // `matchingTargets.count` can't be 0 because // `targets(named:)` would throw an error for that. guard matchingTargets.count == 1 else { - throw Error.tooManyTargetsMatchingTargetName(targetNames: matchingTargets.map(\.name)) + throw PluginError.tooManyTargetsMatchingTargetName(targetNames: matchingTargets.map(\.name)) } let target = matchingTargets[0] guard let swiftTarget = target as? SwiftSourceModuleTarget else { - throw Error.incompatibleTarget(targetName: target.name) + throw PluginError.incompatibleTarget(targetName: target.name) } return try runCommand( targetWorkingDirectory: target.directory, @@ -139,16 +74,16 @@ extension SwiftOpenAPIGeneratorPlugin: XcodeCommandPlugin { arguments: [String] ) throws { guard arguments.count == 2, arguments[0] == "--target" else { - throw Error.badArguments(arguments: arguments) + throw PluginError.badArguments(arguments: arguments) } let targetName = arguments[1] - guard let xcodeTarget = context.xcodeProject.targets.first(where: { - $0.displayName == targetName - }) else { - throw Error.noTargetsMatchingTargetName(targetName: targetName) + guard let xcodeTarget = context.xcodeProject.targets.first( + where: { $0.displayName == targetName } + ) else { + throw PluginError.noTargetsMatchingTargetName(targetName: targetName) } guard let target = xcodeTarget as? SourceModuleTarget else { - throw Error.incompatibleTarget(targetName: targetName) + throw PluginError.incompatibleTarget(targetName: targetName) } return try runCommand( targetWorkingDirectory: target.directory, diff --git a/Plugins/PluginsShared/PluginError.swift b/Plugins/PluginsShared/PluginError.swift new file mode 100644 index 00000000..d2392222 --- /dev/null +++ b/Plugins/PluginsShared/PluginError.swift @@ -0,0 +1,45 @@ +import PackagePlugin +import Foundation + +enum PluginError: Swift.Error, CustomStringConvertible, LocalizedError { + case incompatibleTarget(targetName: String) + case badArguments(arguments: [String]) + // The description is only suitable for Xcode, as it's only thrown in Xcode plugins. + case noTargetsMatchingTargetName(targetName: String) + // The description is not suitable for Xcode, as it's not thrown in Xcode plugins. + case tooManyTargetsMatchingTargetName(targetNames: [String]) + case noConfigFound(targetName: String) + case noDocumentFound(targetName: String) + case multiConfigFound(targetName: String, files: [Path]) + case multiDocumentFound(targetName: String, files: [Path]) + + var description: String { + switch self { + case .incompatibleTarget(let targetName): + return + "Incompatible target called '\(targetName)'. Only Swift source targets can be used with the Swift OpenAPI generator plugin." + case .badArguments(let arguments): + return "Bad arguments provided: \(arguments). On Xcode, use Xcode's run plugin UI to choose a specific target. On CLI, pass a specific target's name to the command like so: '--target TARGET_NAME'" + case .noTargetsMatchingTargetName(let targetName): + return "No target called '\(targetName)' were found. Use Xcode's UI to choose a single specific target before triggering the command plugin." + case .tooManyTargetsMatchingTargetName(let targetNames): + return "Too many targets found matching the provided target name: '\(targetNames)'. Target name must be specific enough for the plugin to only find a single target." + case .noConfigFound(let targetName): + return + "No config file found in the target named '\(targetName)'. Add a file called 'openapi-generator-config.yaml' or 'openapi-generator-config.yml' to the target's source directory. See documentation for details." + case .noDocumentFound(let targetName): + return + "No OpenAPI document found in the target named '\(targetName)'. Add a file called 'openapi.yaml', 'openapi.yml' or 'openapi.json' (can also be a symlink) to the target's source directory. See documentation for details." + case .multiConfigFound(let targetName, let files): + return + "Multiple config files found in the target named '\(targetName)', but exactly one is required. Found \(files.map(\.description).joined(separator: " "))." + case .multiDocumentFound(let targetName, let files): + return + "Multiple OpenAPI documents found in the target named '\(targetName)', but exactly one is required. Found \(files.map(\.description).joined(separator: " "))." + } + } + + var errorDescription: String? { + description + } +} diff --git a/Plugins/PluginsShared/PluginUtils.swift b/Plugins/PluginsShared/PluginUtils.swift new file mode 100644 index 00000000..cb832e3b --- /dev/null +++ b/Plugins/PluginsShared/PluginUtils.swift @@ -0,0 +1,59 @@ +import PackagePlugin + +enum PluginUtils { + + struct ValidatedInputs { + let doc: Path + let config: Path + let genSourcesDir: Path + let arguments: [String] + let tool: PluginContext.Tool + } + + private static var supportedConfigFiles: Set { Set(["yaml", "yml"].map { "openapi-generator-config." + $0 }) } + private static var supportedDocFiles: Set { Set(["yaml", "yml", "json"].map { "openapi." + $0 }) } + + static func validateInputs( + workingDirectory: Path, + tool: (String) throws -> PluginContext.Tool, + sourceFiles: FileList, + targetName: String + ) throws -> ValidatedInputs { + let inputFiles = sourceFiles + let matchedConfigs = inputFiles.filter { supportedConfigFiles.contains($0.path.lastComponent) }.map(\.path) + guard matchedConfigs.count > 0 else { + throw PluginError.noConfigFound(targetName: targetName) + } + guard matchedConfigs.count == 1 else { + throw PluginError.multiConfigFound(targetName: targetName, files: matchedConfigs) + } + let config = matchedConfigs[0] + + let matchedDocs = inputFiles.filter { supportedDocFiles.contains($0.path.lastComponent) }.map(\.path) + guard matchedDocs.count > 0 else { + throw PluginError.noDocumentFound(targetName: targetName) + } + guard matchedDocs.count == 1 else { + throw PluginError.multiDocumentFound(targetName: targetName, files: matchedDocs) + } + let doc = matchedDocs[0] + let genSourcesDir = workingDirectory.appending("GeneratedSources") + + let arguments = [ + "generate", "\(doc)", + "--config", "\(config)", + "--output-directory", "\(genSourcesDir)", + "--invocation-kind", "CommandPlugin" + ] + + let tool = try tool("swift-openapi-generator") + + return ValidatedInputs( + doc: doc, + config: config, + genSourcesDir: genSourcesDir, + arguments: arguments, + tool: tool + ) + } +} From 42c73f370d0ceddd7ef99e59cba5cb777a3d2335 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Mon, 3 Jul 2023 16:48:29 +0330 Subject: [PATCH 021/100] rename and fix 'invocation-kind' arg to 'invoked-from' --- Plugins/OpenAPIGenerator/plugin.swift | 3 ++- Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift | 3 ++- Plugins/PluginsShared/PluginUtils.swift | 5 +++-- .../swift-openapi-generator/GenerateCommand.swift | 6 +++--- .../GenerateOptions+runGenerator.swift | 8 ++++---- ...InvocationKind.swift => InvocationSource.swift} | 2 +- Sources/swift-openapi-generator/runGenerator.swift | 14 +++++++------- 7 files changed, 22 insertions(+), 19 deletions(-) rename Sources/swift-openapi-generator/{InvocationKind.swift => InvocationSource.swift} (57%) diff --git a/Plugins/OpenAPIGenerator/plugin.swift b/Plugins/OpenAPIGenerator/plugin.swift index bad05ff1..85ef627a 100644 --- a/Plugins/OpenAPIGenerator/plugin.swift +++ b/Plugins/OpenAPIGenerator/plugin.swift @@ -29,7 +29,8 @@ struct SwiftOpenAPIGeneratorPlugin { workingDirectory: pluginWorkDirectory, tool: tool, sourceFiles: sourceFiles, - targetName: targetName + targetName: targetName, + invokedFrom: "BuildToolPlugin" ) let outputFiles: [Path] = GeneratorMode.allCases.map { inputs.genSourcesDir.appending($0.outputFileNameSuffix) } diff --git a/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift b/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift index 979e8fe1..569b0d83 100644 --- a/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift @@ -26,7 +26,8 @@ struct SwiftOpenAPIGeneratorPlugin { workingDirectory: targetWorkingDirectory, tool: tool, sourceFiles: sourceFiles, - targetName: targetName + targetName: targetName, + invokedFrom: "CommandPlugin" ) let toolUrl = URL(fileURLWithPath: inputs.tool.path.string) diff --git a/Plugins/PluginsShared/PluginUtils.swift b/Plugins/PluginsShared/PluginUtils.swift index cb832e3b..b4ee1f4b 100644 --- a/Plugins/PluginsShared/PluginUtils.swift +++ b/Plugins/PluginsShared/PluginUtils.swift @@ -17,7 +17,8 @@ enum PluginUtils { workingDirectory: Path, tool: (String) throws -> PluginContext.Tool, sourceFiles: FileList, - targetName: String + targetName: String, + invokedFrom: String ) throws -> ValidatedInputs { let inputFiles = sourceFiles let matchedConfigs = inputFiles.filter { supportedConfigFiles.contains($0.path.lastComponent) }.map(\.path) @@ -43,7 +44,7 @@ enum PluginUtils { "generate", "\(doc)", "--config", "\(config)", "--output-directory", "\(genSourcesDir)", - "--invocation-kind", "CommandPlugin" + "--invoked-from", invokedFrom ] let tool = try tool("swift-openapi-generator") diff --git a/Sources/swift-openapi-generator/GenerateCommand.swift b/Sources/swift-openapi-generator/GenerateCommand.swift index 7935ba27..10bc1638 100644 --- a/Sources/swift-openapi-generator/GenerateCommand.swift +++ b/Sources/swift-openapi-generator/GenerateCommand.swift @@ -46,14 +46,14 @@ struct _GenerateCommand: AsyncParsableCommand { help: "Whether this invocation is from the SwiftPM plugin. We always need to produce all files when invoked from the plugin. Non-requested modes produce empty files." ) - var invocationKind: InvocationKind = .CLI + var invokedFrom: InvocationSource = .CLI func run() async throws { try generate.runGenerator( outputDirectory: outputDirectory, - invocationKind: invocationKind + invokedFrom: invokedFrom ) } } -extension InvocationKind: ExpressibleByArgument { } +extension InvocationSource: ExpressibleByArgument { } diff --git a/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift b/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift index 606cffac..0c1a3f84 100644 --- a/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift +++ b/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift @@ -29,11 +29,11 @@ extension _GenerateOptions { /// a limitation of the build system used by SwiftPM under the hood. func runGenerator( outputDirectory: URL, - invocationKind: InvocationKind + invokedFrom: InvocationSource ) throws { let config = try loadedConfig() - switch invocationKind { + switch invokedFrom { case .BuildToolPlugin: guard (config?.pluginMode ?? .BuildTool) == .BuildTool else { print("Plugin disabled for BuildTool plugins. Will clean up files if there are any leftovers from previous builds.") @@ -79,7 +79,7 @@ extension _GenerateOptions { - Output directory: \(outputDirectory.path) - Diagnostics output path: \(diagnosticsOutputPath?.path ?? "") - Current directory: \(FileManager.default.currentDirectoryPath) - - Invocation kind: \(invocationKind.rawValue) + - Invocation kind: \(invokedFrom.rawValue) - Additional imports: \(resolvedAdditionalImports.isEmpty ? "" : resolvedAdditionalImports.joined(separator: ", ")) """ ) @@ -87,7 +87,7 @@ extension _GenerateOptions { try _Tool.runGenerator( doc: doc, configs: configs, - invocationKind: invocationKind, + invokedFrom: invokedFrom, outputDirectory: outputDirectory, diagnostics: diagnostics ) diff --git a/Sources/swift-openapi-generator/InvocationKind.swift b/Sources/swift-openapi-generator/InvocationSource.swift similarity index 57% rename from Sources/swift-openapi-generator/InvocationKind.swift rename to Sources/swift-openapi-generator/InvocationSource.swift index f0350b51..f51f37a4 100644 --- a/Sources/swift-openapi-generator/InvocationKind.swift +++ b/Sources/swift-openapi-generator/InvocationSource.swift @@ -1,5 +1,5 @@ -public enum InvocationKind: String, Codable { +public enum InvocationSource: String, Codable { case BuildToolPlugin case CommandPlugin case CLI diff --git a/Sources/swift-openapi-generator/runGenerator.swift b/Sources/swift-openapi-generator/runGenerator.swift index 38bd8d96..bba69356 100644 --- a/Sources/swift-openapi-generator/runGenerator.swift +++ b/Sources/swift-openapi-generator/runGenerator.swift @@ -29,7 +29,7 @@ extension _Tool { static func runGenerator( doc: URL, configs: [Config], - invocationKind: InvocationKind, + invokedFrom: InvocationSource, outputDirectory: URL, diagnostics: DiagnosticCollector ) throws { @@ -49,14 +49,14 @@ extension _Tool { docData: docData, config: config, outputDirectory: outputDirectory, - outputFileName: fullFileName(config.mode, invocationKind: invocationKind), + outputFileName: fullFileName(config.mode, invokedFrom: invokedFrom), diagnostics: diagnostics ) } // Swift expects us to always create these files in BuildTool plugins, // so we create the unused files, but empty. - if invocationKind == .BuildToolPlugin { + if invokedFrom == .BuildToolPlugin { let nonGeneratedModes = Set(GeneratorMode.allCases).subtracting(configs.map(\.mode)) for mode in nonGeneratedModes.sorted() { try replaceFileContents( @@ -136,14 +136,14 @@ extension _Tool { } } - static func fullFileName(_ mode: GeneratorMode, invocationKind: InvocationKind) -> String { - let outputFileNamePrefix = invocationKind == .BuildToolPlugin ? "" : "Generated_" + static func fullFileName(_ mode: GeneratorMode, invokedFrom: InvocationSource) -> String { + let outputFileNamePrefix = invokedFrom == .BuildToolPlugin ? "" : "Generated_" return outputFileNamePrefix + mode.outputFileNameSuffix } static func runBuildToolPluginCleanup(outputDirectory: URL) throws { for mode in GeneratorMode.allCases { - let fileName = fullFileName(mode, invocationKind: .BuildToolPlugin) + let fileName = fullFileName(mode, invokedFrom: .BuildToolPlugin) // Swift expects us to always create these files, so we create them but empty. try replaceFileContents( inDirectory: outputDirectory, @@ -161,7 +161,7 @@ extension _Tool { // Remove each file for mode in GeneratorMode.allCases { - let fileName = fullFileName(mode, invocationKind: .CommandPlugin) + let fileName = fullFileName(mode, invokedFrom: .CommandPlugin) let path = outputDirectory.appendingPathComponent(fileName) if fm.fileExists(atPath: path.path) { try fm.removeItem(at: path) From 253e17fe2685d43d7b7e71afe0397f3574b5f0ce Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Mon, 3 Jul 2023 16:49:43 +0330 Subject: [PATCH 022/100] InvocationSource -> internal --- Sources/swift-openapi-generator/InvocationSource.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/swift-openapi-generator/InvocationSource.swift b/Sources/swift-openapi-generator/InvocationSource.swift index f51f37a4..6614ec7b 100644 --- a/Sources/swift-openapi-generator/InvocationSource.swift +++ b/Sources/swift-openapi-generator/InvocationSource.swift @@ -1,5 +1,5 @@ -public enum InvocationSource: String, Codable { +enum InvocationSource: String, Codable { case BuildToolPlugin case CommandPlugin case CLI From 31365ed79407e3a41164c2f16576fd76b5d8bfeb Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Mon, 3 Jul 2023 17:25:54 +0330 Subject: [PATCH 023/100] better (or worse) InvocationSource handling --- .../OpenAPIGenerator/InvocationSource.swift | 1 + Plugins/OpenAPIGenerator/plugin.swift | 4 ++-- .../InvocationSource.swift | 1 + .../plugin.swift | 2 +- Plugins/PluginsShared/PluginUtils.swift | 4 ++-- Sources/_OpenAPIGeneratorCore/Config.swift | 6 +++++- .../_OpenAPIGeneratorCore/GeneratorMode.swift | 21 +++++++++++++------ .../InvocationSource.swift | 2 +- .../ClientTranslator/ClientTranslator.swift | 2 +- .../ServerTranslator/ServerTranslator.swift | 2 +- .../TypesTranslator/TypesFileTranslator.swift | 2 +- .../GenerateCommand.swift | 4 ++-- .../GenerateOptions+runGenerator.swift | 13 ++++++------ .../runGenerator.swift | 17 ++++++--------- .../Parser/Test_YamsParser.swift | 2 +- .../TestUtilities.swift | 11 ++++++---- .../Test_translateStructBlueprint.swift | 3 ++- .../Test_isSchemaSupported.swift | 2 +- .../ReferenceTest.swift | 5 ++++- 19 files changed, 61 insertions(+), 43 deletions(-) create mode 120000 Plugins/OpenAPIGenerator/InvocationSource.swift create mode 120000 Plugins/OpenAPIGeneratorCommandPlugin/InvocationSource.swift rename Sources/{swift-openapi-generator => _OpenAPIGeneratorCore}/InvocationSource.swift (57%) diff --git a/Plugins/OpenAPIGenerator/InvocationSource.swift b/Plugins/OpenAPIGenerator/InvocationSource.swift new file mode 120000 index 00000000..4fa1c9d9 --- /dev/null +++ b/Plugins/OpenAPIGenerator/InvocationSource.swift @@ -0,0 +1 @@ +../../Sources/_OpenAPIGeneratorCore/InvocationSource.swift \ No newline at end of file diff --git a/Plugins/OpenAPIGenerator/plugin.swift b/Plugins/OpenAPIGenerator/plugin.swift index 85ef627a..56a31f22 100644 --- a/Plugins/OpenAPIGenerator/plugin.swift +++ b/Plugins/OpenAPIGenerator/plugin.swift @@ -30,10 +30,10 @@ struct SwiftOpenAPIGeneratorPlugin { tool: tool, sourceFiles: sourceFiles, targetName: targetName, - invokedFrom: "BuildToolPlugin" + invocationSource: .BuildToolPlugin ) - let outputFiles: [Path] = GeneratorMode.allCases.map { inputs.genSourcesDir.appending($0.outputFileNameSuffix) } + let outputFiles: [Path] = GeneratorMode.allCases.map { inputs.genSourcesDir.appending($0.fileName(for: .BuildToolPlugin)) } return [ .buildCommand( displayName: "Running swift-openapi-generator", diff --git a/Plugins/OpenAPIGeneratorCommandPlugin/InvocationSource.swift b/Plugins/OpenAPIGeneratorCommandPlugin/InvocationSource.swift new file mode 120000 index 00000000..4fa1c9d9 --- /dev/null +++ b/Plugins/OpenAPIGeneratorCommandPlugin/InvocationSource.swift @@ -0,0 +1 @@ +../../Sources/_OpenAPIGeneratorCore/InvocationSource.swift \ No newline at end of file diff --git a/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift b/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift index 569b0d83..f3e0a36f 100644 --- a/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift @@ -27,7 +27,7 @@ struct SwiftOpenAPIGeneratorPlugin { tool: tool, sourceFiles: sourceFiles, targetName: targetName, - invokedFrom: "CommandPlugin" + invocationSource: .CommandPlugin ) let toolUrl = URL(fileURLWithPath: inputs.tool.path.string) diff --git a/Plugins/PluginsShared/PluginUtils.swift b/Plugins/PluginsShared/PluginUtils.swift index b4ee1f4b..4574220e 100644 --- a/Plugins/PluginsShared/PluginUtils.swift +++ b/Plugins/PluginsShared/PluginUtils.swift @@ -18,7 +18,7 @@ enum PluginUtils { tool: (String) throws -> PluginContext.Tool, sourceFiles: FileList, targetName: String, - invokedFrom: String + invocationSource: InvocationSource ) throws -> ValidatedInputs { let inputFiles = sourceFiles let matchedConfigs = inputFiles.filter { supportedConfigFiles.contains($0.path.lastComponent) }.map(\.path) @@ -44,7 +44,7 @@ enum PluginUtils { "generate", "\(doc)", "--config", "\(config)", "--output-directory", "\(genSourcesDir)", - "--invoked-from", invokedFrom + "--invoked-from", "\(invocationSource)" ] let tool = try tool("swift-openapi-generator") diff --git a/Sources/_OpenAPIGeneratorCore/Config.swift b/Sources/_OpenAPIGeneratorCore/Config.swift index a13f0446..e61ff3d3 100644 --- a/Sources/_OpenAPIGeneratorCore/Config.swift +++ b/Sources/_OpenAPIGeneratorCore/Config.swift @@ -28,14 +28,18 @@ public struct Config { /// Additional imports to add to each generated file. public var additionalImports: [String] + /// Where this command has been triggered from. + public var invocationSource: InvocationSource + /// Creates a configuration with the specified generator mode and imports. /// - Parameters: /// - mode: The mode to use for generation. /// - additionalImports: Additional imports to add to each generated /// file. - public init(mode: GeneratorMode, additionalImports: [String] = []) { + public init(mode: GeneratorMode, additionalImports: [String] = [], invocationSource: InvocationSource) { self.mode = mode self.additionalImports = additionalImports + self.invocationSource = invocationSource } } diff --git a/Sources/_OpenAPIGeneratorCore/GeneratorMode.swift b/Sources/_OpenAPIGeneratorCore/GeneratorMode.swift index ff59dd4c..2ec2f1be 100644 --- a/Sources/_OpenAPIGeneratorCore/GeneratorMode.swift +++ b/Sources/_OpenAPIGeneratorCore/GeneratorMode.swift @@ -35,9 +35,7 @@ public enum GeneratorMode: String, Codable, CaseIterable { } extension GeneratorMode { - - /// The Swift file name including its file extension. - public var outputFileNameSuffix: String { + fileprivate var fileNameSuffix: String { switch self { case .types: return "Types.swift" @@ -48,9 +46,14 @@ extension GeneratorMode { } } - /// The Swift file names for all supported generator mode values. - public static var allOutputFileNameSuffixes: [String] { - GeneratorMode.allCases.map(\.outputFileNameSuffix) + /// The Swift file name including its file extension. + public func fileName(for invocationSource: InvocationSource) -> String { + switch invocationSource { + case .BuildToolPlugin, .CLI: + return self.fileNameSuffix + case .CommandPlugin: + return "Generated_\(self.fileNameSuffix)" + } } /// Defines an order in which generators should be run. @@ -66,6 +69,12 @@ extension GeneratorMode { } } +extension Array where Element == GeneratorMode { + public var outputFileNameSuffixes: [String] { + self.map(\.fileNameSuffix) + } +} + extension GeneratorMode: Comparable { public static func < (lhs: GeneratorMode, rhs: GeneratorMode) -> Bool { lhs.order < rhs.order diff --git a/Sources/swift-openapi-generator/InvocationSource.swift b/Sources/_OpenAPIGeneratorCore/InvocationSource.swift similarity index 57% rename from Sources/swift-openapi-generator/InvocationSource.swift rename to Sources/_OpenAPIGeneratorCore/InvocationSource.swift index 6614ec7b..f51f37a4 100644 --- a/Sources/swift-openapi-generator/InvocationSource.swift +++ b/Sources/_OpenAPIGeneratorCore/InvocationSource.swift @@ -1,5 +1,5 @@ -enum InvocationSource: String, Codable { +public enum InvocationSource: String, Codable { case BuildToolPlugin case CommandPlugin case CLI diff --git a/Sources/_OpenAPIGeneratorCore/Translator/ClientTranslator/ClientTranslator.swift b/Sources/_OpenAPIGeneratorCore/Translator/ClientTranslator/ClientTranslator.swift index c741af32..5f3705cd 100644 --- a/Sources/_OpenAPIGeneratorCore/Translator/ClientTranslator/ClientTranslator.swift +++ b/Sources/_OpenAPIGeneratorCore/Translator/ClientTranslator/ClientTranslator.swift @@ -148,7 +148,7 @@ struct ClientFileTranslator: FileTranslator { return StructuredSwiftRepresentation( file: .init( - name: GeneratorMode.client.outputFileNameSuffix, + name: GeneratorMode.client.fileName(for: config.invocationSource), contents: .init( topComment: topComment, imports: imports, diff --git a/Sources/_OpenAPIGeneratorCore/Translator/ServerTranslator/ServerTranslator.swift b/Sources/_OpenAPIGeneratorCore/Translator/ServerTranslator/ServerTranslator.swift index 3576e044..11e4c11a 100644 --- a/Sources/_OpenAPIGeneratorCore/Translator/ServerTranslator/ServerTranslator.swift +++ b/Sources/_OpenAPIGeneratorCore/Translator/ServerTranslator/ServerTranslator.swift @@ -128,7 +128,7 @@ struct ServerFileTranslator: FileTranslator { return StructuredSwiftRepresentation( file: .init( - name: GeneratorMode.server.outputFileNameSuffix, + name: GeneratorMode.server.fileName(for: config.invocationSource), contents: .init( topComment: topComment, imports: imports, diff --git a/Sources/_OpenAPIGeneratorCore/Translator/TypesTranslator/TypesFileTranslator.swift b/Sources/_OpenAPIGeneratorCore/Translator/TypesTranslator/TypesFileTranslator.swift index c5faaaa9..618f8902 100644 --- a/Sources/_OpenAPIGeneratorCore/Translator/TypesTranslator/TypesFileTranslator.swift +++ b/Sources/_OpenAPIGeneratorCore/Translator/TypesTranslator/TypesFileTranslator.swift @@ -65,7 +65,7 @@ struct TypesFileTranslator: FileTranslator { return StructuredSwiftRepresentation( file: .init( - name: GeneratorMode.types.outputFileNameSuffix, + name: GeneratorMode.types.fileName(for: config.invocationSource), contents: typesFile ) ) diff --git a/Sources/swift-openapi-generator/GenerateCommand.swift b/Sources/swift-openapi-generator/GenerateCommand.swift index 10bc1638..30cc2f70 100644 --- a/Sources/swift-openapi-generator/GenerateCommand.swift +++ b/Sources/swift-openapi-generator/GenerateCommand.swift @@ -38,7 +38,7 @@ struct _GenerateCommand: AsyncParsableCommand { @Option( help: - "Output directory where the generated files are written. Warning: Replaces any existing files with the same filename. Reserved filename suffixes: \(GeneratorMode.allOutputFileNameSuffixes.joined(separator: ", "))" + "Output directory where the generated files are written. Warning: Replaces any existing files with the same filename. Reserved filename suffixes: \(GeneratorMode.allCases.outputFileNameSuffixes.joined(separator: ", "))" ) var outputDirectory: URL = URL(fileURLWithPath: FileManager.default.currentDirectoryPath) @@ -51,7 +51,7 @@ struct _GenerateCommand: AsyncParsableCommand { func run() async throws { try generate.runGenerator( outputDirectory: outputDirectory, - invokedFrom: invokedFrom + invocationSource: invokedFrom ) } } diff --git a/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift b/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift index 0c1a3f84..1534354a 100644 --- a/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift +++ b/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift @@ -29,11 +29,11 @@ extension _GenerateOptions { /// a limitation of the build system used by SwiftPM under the hood. func runGenerator( outputDirectory: URL, - invokedFrom: InvocationSource + invocationSource: InvocationSource ) throws { let config = try loadedConfig() - switch invokedFrom { + switch invocationSource { case .BuildToolPlugin: guard (config?.pluginMode ?? .BuildTool) == .BuildTool else { print("Plugin disabled for BuildTool plugins. Will clean up files if there are any leftovers from previous builds.") @@ -54,7 +54,8 @@ extension _GenerateOptions { let configs: [Config] = sortedModes.map { .init( mode: $0, - additionalImports: resolvedAdditionalImports + additionalImports: resolvedAdditionalImports, + invocationSource: invocationSource ) } let diagnostics: DiagnosticCollector @@ -75,11 +76,11 @@ extension _GenerateOptions { - OpenAPI document path: \(doc.path) - Configuration path: \(self.config?.path ?? "") - Generator modes: \(sortedModes.map(\.rawValue).joined(separator: ", ")) - - Output file name suffixes: \(sortedModes.map(\.outputFileNameSuffix).joined(separator: ", ")) + - Output file name suffixes: \(sortedModes.outputFileNameSuffixes.joined(separator: ", ")) - Output directory: \(outputDirectory.path) - Diagnostics output path: \(diagnosticsOutputPath?.path ?? "") - Current directory: \(FileManager.default.currentDirectoryPath) - - Invocation kind: \(invokedFrom.rawValue) + - Invocation kind: \(invocationSource.rawValue) - Additional imports: \(resolvedAdditionalImports.isEmpty ? "" : resolvedAdditionalImports.joined(separator: ", ")) """ ) @@ -87,7 +88,7 @@ extension _GenerateOptions { try _Tool.runGenerator( doc: doc, configs: configs, - invokedFrom: invokedFrom, + invocationSource: invocationSource, outputDirectory: outputDirectory, diagnostics: diagnostics ) diff --git a/Sources/swift-openapi-generator/runGenerator.swift b/Sources/swift-openapi-generator/runGenerator.swift index bba69356..0c3c4096 100644 --- a/Sources/swift-openapi-generator/runGenerator.swift +++ b/Sources/swift-openapi-generator/runGenerator.swift @@ -29,7 +29,7 @@ extension _Tool { static func runGenerator( doc: URL, configs: [Config], - invokedFrom: InvocationSource, + invocationSource: InvocationSource, outputDirectory: URL, diagnostics: DiagnosticCollector ) throws { @@ -49,19 +49,19 @@ extension _Tool { docData: docData, config: config, outputDirectory: outputDirectory, - outputFileName: fullFileName(config.mode, invokedFrom: invokedFrom), + outputFileName: config.mode.fileName(for: invocationSource), diagnostics: diagnostics ) } // Swift expects us to always create these files in BuildTool plugins, // so we create the unused files, but empty. - if invokedFrom == .BuildToolPlugin { + if invocationSource == .BuildToolPlugin { let nonGeneratedModes = Set(GeneratorMode.allCases).subtracting(configs.map(\.mode)) for mode in nonGeneratedModes.sorted() { try replaceFileContents( inDirectory: outputDirectory, - fileName: mode.outputFileNameSuffix, + fileName: mode.fileName(for: .BuildToolPlugin), with: { Data() } ) } @@ -136,14 +136,9 @@ extension _Tool { } } - static func fullFileName(_ mode: GeneratorMode, invokedFrom: InvocationSource) -> String { - let outputFileNamePrefix = invokedFrom == .BuildToolPlugin ? "" : "Generated_" - return outputFileNamePrefix + mode.outputFileNameSuffix - } - static func runBuildToolPluginCleanup(outputDirectory: URL) throws { for mode in GeneratorMode.allCases { - let fileName = fullFileName(mode, invokedFrom: .BuildToolPlugin) + let fileName = mode.fileName(for: .BuildToolPlugin) // Swift expects us to always create these files, so we create them but empty. try replaceFileContents( inDirectory: outputDirectory, @@ -161,7 +156,7 @@ extension _Tool { // Remove each file for mode in GeneratorMode.allCases { - let fileName = fullFileName(mode, invokedFrom: .CommandPlugin) + let fileName = mode.fileName(for: .CommandPlugin) let path = outputDirectory.appendingPathComponent(fileName) if fm.fileExists(atPath: path.path) { try fm.removeItem(at: path) diff --git a/Tests/OpenAPIGeneratorCoreTests/Parser/Test_YamsParser.swift b/Tests/OpenAPIGeneratorCoreTests/Parser/Test_YamsParser.swift index 18d9f52b..90a785e8 100644 --- a/Tests/OpenAPIGeneratorCoreTests/Parser/Test_YamsParser.swift +++ b/Tests/OpenAPIGeneratorCoreTests/Parser/Test_YamsParser.swift @@ -130,7 +130,7 @@ final class Test_YamsParser: Test_Core { absolutePath: URL(fileURLWithPath: "/foo.yaml"), contents: Data(yaml.utf8) ), - config: .init(mode: .types), + config: .init(mode: .types, invocationSource: .BuildToolPlugin), diagnostics: PrintingDiagnosticCollector() ) } diff --git a/Tests/OpenAPIGeneratorCoreTests/TestUtilities.swift b/Tests/OpenAPIGeneratorCoreTests/TestUtilities.swift index bc4b5076..2cc4b3d1 100644 --- a/Tests/OpenAPIGeneratorCoreTests/TestUtilities.swift +++ b/Tests/OpenAPIGeneratorCoreTests/TestUtilities.swift @@ -25,20 +25,23 @@ class Test_Core: XCTestCase { func makeTranslator( components: OpenAPI.Components = .noComponents, - diagnostics: DiagnosticCollector = PrintingDiagnosticCollector() + diagnostics: DiagnosticCollector = PrintingDiagnosticCollector(), + invocationSource: InvocationSource = .BuildToolPlugin ) -> FileTranslator { makeTypesTranslator( components: components, - diagnostics: diagnostics + diagnostics: diagnostics, + invocationSource: invocationSource ) } func makeTypesTranslator( components: OpenAPI.Components = .noComponents, - diagnostics: DiagnosticCollector = PrintingDiagnosticCollector() + diagnostics: DiagnosticCollector = PrintingDiagnosticCollector(), + invocationSource: InvocationSource = .BuildToolPlugin ) -> TypesFileTranslator { TypesFileTranslator( - config: .init(mode: .types), + config: .init(mode: .types, invocationSource: invocationSource), diagnostics: diagnostics, components: components ) diff --git a/Tests/OpenAPIGeneratorCoreTests/Translator/CommonTranslations/Test_translateStructBlueprint.swift b/Tests/OpenAPIGeneratorCoreTests/Translator/CommonTranslations/Test_translateStructBlueprint.swift index 4c642342..bad0a68c 100644 --- a/Tests/OpenAPIGeneratorCoreTests/Translator/CommonTranslations/Test_translateStructBlueprint.swift +++ b/Tests/OpenAPIGeneratorCoreTests/Translator/CommonTranslations/Test_translateStructBlueprint.swift @@ -55,7 +55,8 @@ final class Test_translateStructBlueprint: Test_Core { func testDeprecatedStruct() throws { let blueprint = StructBlueprint(isDeprecated: true, typeName: Self.testTypeName, properties: []) - let decl = makeTypesTranslator().translateStructBlueprint(blueprint) + let decl = makeTypesTranslator() + .translateStructBlueprint(blueprint) XCTAssertEqual(decl.strippingTopComment.info.kind, .deprecated) } diff --git a/Tests/OpenAPIGeneratorCoreTests/Translator/TypeAssignment/Test_isSchemaSupported.swift b/Tests/OpenAPIGeneratorCoreTests/Translator/TypeAssignment/Test_isSchemaSupported.swift index 10c77b4c..f8bcd49f 100644 --- a/Tests/OpenAPIGeneratorCoreTests/Translator/TypeAssignment/Test_isSchemaSupported.swift +++ b/Tests/OpenAPIGeneratorCoreTests/Translator/TypeAssignment/Test_isSchemaSupported.swift @@ -19,7 +19,7 @@ class Test_isSchemaSupported: XCTestCase { var translator: FileTranslator { TypesFileTranslator( - config: .init(mode: .types), + config: .init(mode: .types, invocationSource: .BuildToolPlugin), diagnostics: PrintingDiagnosticCollector(), components: .init(schemas: [ "Foo": .string, diff --git a/Tests/OpenAPIGeneratorReferenceTests/ReferenceTest.swift b/Tests/OpenAPIGeneratorReferenceTests/ReferenceTest.swift index 9927dd08..443750f7 100644 --- a/Tests/OpenAPIGeneratorReferenceTests/ReferenceTest.swift +++ b/Tests/OpenAPIGeneratorReferenceTests/ReferenceTest.swift @@ -20,6 +20,7 @@ struct TestConfig: Encodable { var docFilePath: String var mode: GeneratorMode var additionalImports: [String]? + var invocationSource: InvocationSource var referenceOutputDirectory: String } @@ -27,7 +28,8 @@ extension TestConfig { var asConfig: Config { .init( mode: mode, - additionalImports: additionalImports ?? [] + additionalImports: additionalImports ?? [], + invocationSource: invocationSource ) } } @@ -136,6 +138,7 @@ class ReferenceTests: XCTestCase { docFilePath: "Docs/\(name.fileName)", mode: mode, additionalImports: [], + invocationSource: .BuildToolPlugin, referenceOutputDirectory: "ReferenceSources/\(name.directoryName)" ) ) From 617edfee75b850c9b9a7d35799df662d659e20f0 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Tue, 4 Jul 2023 03:24:06 +0330 Subject: [PATCH 024/100] remove a lot of unneeded complexity --- .../OpenAPIGenerator/InvocationSource.swift | 1 - Plugins/OpenAPIGenerator/PluginError.swift | 1 - Plugins/OpenAPIGenerator/PluginUtils.swift | 1 - Plugins/OpenAPIGenerator/PluginsShared | 1 + Plugins/OpenAPIGenerator/plugin.swift | 5 +---- .../InvocationSource.swift | 1 - .../PluginError.swift | 1 - .../PluginUtils.swift | 1 - .../PluginsShared | 1 + Plugins/PluginsShared/InvocationSource.swift | 1 + Plugins/PluginsShared/PluginUtils.swift | 11 ++++++---- Sources/_OpenAPIGeneratorCore/Config.swift | 6 +----- .../_OpenAPIGeneratorCore/GeneratorMode.swift | 21 ++++++------------- .../ClientTranslator/ClientTranslator.swift | 2 +- .../ServerTranslator/ServerTranslator.swift | 2 +- .../TypesTranslator/TypesFileTranslator.swift | 2 +- .../GenerateCommand.swift | 2 +- .../GenerateOptions+runGenerator.swift | 5 ++--- .../InvocationSource.swift | 2 +- .../runGenerator.swift | 8 +++---- .../Parser/Test_YamsParser.swift | 2 +- .../TestUtilities.swift | 11 ++++------ .../Test_isSchemaSupported.swift | 2 +- .../ReferenceTest.swift | 5 +---- 24 files changed, 36 insertions(+), 59 deletions(-) delete mode 120000 Plugins/OpenAPIGenerator/InvocationSource.swift delete mode 120000 Plugins/OpenAPIGenerator/PluginError.swift delete mode 120000 Plugins/OpenAPIGenerator/PluginUtils.swift create mode 120000 Plugins/OpenAPIGenerator/PluginsShared delete mode 120000 Plugins/OpenAPIGeneratorCommandPlugin/InvocationSource.swift delete mode 120000 Plugins/OpenAPIGeneratorCommandPlugin/PluginError.swift delete mode 120000 Plugins/OpenAPIGeneratorCommandPlugin/PluginUtils.swift create mode 120000 Plugins/OpenAPIGeneratorCommandPlugin/PluginsShared create mode 120000 Plugins/PluginsShared/InvocationSource.swift rename Sources/{_OpenAPIGeneratorCore => swift-openapi-generator}/InvocationSource.swift (57%) diff --git a/Plugins/OpenAPIGenerator/InvocationSource.swift b/Plugins/OpenAPIGenerator/InvocationSource.swift deleted file mode 120000 index 4fa1c9d9..00000000 --- a/Plugins/OpenAPIGenerator/InvocationSource.swift +++ /dev/null @@ -1 +0,0 @@ -../../Sources/_OpenAPIGeneratorCore/InvocationSource.swift \ No newline at end of file diff --git a/Plugins/OpenAPIGenerator/PluginError.swift b/Plugins/OpenAPIGenerator/PluginError.swift deleted file mode 120000 index 2e97a77b..00000000 --- a/Plugins/OpenAPIGenerator/PluginError.swift +++ /dev/null @@ -1 +0,0 @@ -../../Plugins/PluginsShared/PluginError.swift \ No newline at end of file diff --git a/Plugins/OpenAPIGenerator/PluginUtils.swift b/Plugins/OpenAPIGenerator/PluginUtils.swift deleted file mode 120000 index 2d3faf71..00000000 --- a/Plugins/OpenAPIGenerator/PluginUtils.swift +++ /dev/null @@ -1 +0,0 @@ -../../Plugins/PluginsShared/PluginUtils.swift \ No newline at end of file diff --git a/Plugins/OpenAPIGenerator/PluginsShared b/Plugins/OpenAPIGenerator/PluginsShared new file mode 120000 index 00000000..a8c88dc3 --- /dev/null +++ b/Plugins/OpenAPIGenerator/PluginsShared @@ -0,0 +1 @@ +../../Plugins/PluginsShared \ No newline at end of file diff --git a/Plugins/OpenAPIGenerator/plugin.swift b/Plugins/OpenAPIGenerator/plugin.swift index 56a31f22..eb60b08d 100644 --- a/Plugins/OpenAPIGenerator/plugin.swift +++ b/Plugins/OpenAPIGenerator/plugin.swift @@ -16,9 +16,6 @@ import Foundation @main struct SwiftOpenAPIGeneratorPlugin { - private var supportedConfigFiles: Set { Set(["yaml", "yml"].map { "openapi-generator-config." + $0 }) } - private var supportedDocFiles: Set { Set(["yaml", "yml", "json"].map { "openapi." + $0 }) } - func createBuildCommands( pluginWorkDirectory: Path, tool: (String) throws -> PluginContext.Tool, @@ -33,7 +30,7 @@ struct SwiftOpenAPIGeneratorPlugin { invocationSource: .BuildToolPlugin ) - let outputFiles: [Path] = GeneratorMode.allCases.map { inputs.genSourcesDir.appending($0.fileName(for: .BuildToolPlugin)) } + let outputFiles: [Path] = GeneratorMode.allCases.map { inputs.genSourcesDir.appending($0.outputFileName) } return [ .buildCommand( displayName: "Running swift-openapi-generator", diff --git a/Plugins/OpenAPIGeneratorCommandPlugin/InvocationSource.swift b/Plugins/OpenAPIGeneratorCommandPlugin/InvocationSource.swift deleted file mode 120000 index 4fa1c9d9..00000000 --- a/Plugins/OpenAPIGeneratorCommandPlugin/InvocationSource.swift +++ /dev/null @@ -1 +0,0 @@ -../../Sources/_OpenAPIGeneratorCore/InvocationSource.swift \ No newline at end of file diff --git a/Plugins/OpenAPIGeneratorCommandPlugin/PluginError.swift b/Plugins/OpenAPIGeneratorCommandPlugin/PluginError.swift deleted file mode 120000 index 2e97a77b..00000000 --- a/Plugins/OpenAPIGeneratorCommandPlugin/PluginError.swift +++ /dev/null @@ -1 +0,0 @@ -../../Plugins/PluginsShared/PluginError.swift \ No newline at end of file diff --git a/Plugins/OpenAPIGeneratorCommandPlugin/PluginUtils.swift b/Plugins/OpenAPIGeneratorCommandPlugin/PluginUtils.swift deleted file mode 120000 index 2d3faf71..00000000 --- a/Plugins/OpenAPIGeneratorCommandPlugin/PluginUtils.swift +++ /dev/null @@ -1 +0,0 @@ -../../Plugins/PluginsShared/PluginUtils.swift \ No newline at end of file diff --git a/Plugins/OpenAPIGeneratorCommandPlugin/PluginsShared b/Plugins/OpenAPIGeneratorCommandPlugin/PluginsShared new file mode 120000 index 00000000..a8c88dc3 --- /dev/null +++ b/Plugins/OpenAPIGeneratorCommandPlugin/PluginsShared @@ -0,0 +1 @@ +../../Plugins/PluginsShared \ No newline at end of file diff --git a/Plugins/PluginsShared/InvocationSource.swift b/Plugins/PluginsShared/InvocationSource.swift new file mode 120000 index 00000000..a48bb368 --- /dev/null +++ b/Plugins/PluginsShared/InvocationSource.swift @@ -0,0 +1 @@ +../../Sources/swift-openapi-generator/InvocationSource.swift \ No newline at end of file diff --git a/Plugins/PluginsShared/PluginUtils.swift b/Plugins/PluginsShared/PluginUtils.swift index 4574220e..25fe0e73 100644 --- a/Plugins/PluginsShared/PluginUtils.swift +++ b/Plugins/PluginsShared/PluginUtils.swift @@ -1,6 +1,8 @@ import PackagePlugin enum PluginUtils { + private static var supportedConfigFiles: Set { Set(["yaml", "yml"].map { "openapi-generator-config." + $0 }) } + private static var supportedDocFiles: Set { Set(["yaml", "yml", "json"].map { "openapi." + $0 }) } struct ValidatedInputs { let doc: Path @@ -10,9 +12,6 @@ enum PluginUtils { let tool: PluginContext.Tool } - private static var supportedConfigFiles: Set { Set(["yaml", "yml"].map { "openapi-generator-config." + $0 }) } - private static var supportedDocFiles: Set { Set(["yaml", "yml", "json"].map { "openapi." + $0 }) } - static func validateInputs( workingDirectory: Path, tool: (String) throws -> PluginContext.Tool, @@ -49,12 +48,16 @@ enum PluginUtils { let tool = try tool("swift-openapi-generator") - return ValidatedInputs( + let inputs = ValidatedInputs( doc: doc, config: config, genSourcesDir: genSourcesDir, arguments: arguments, tool: tool ) + + print("INPUTS for \(invocationSource)", inputs) + + return inputs } } diff --git a/Sources/_OpenAPIGeneratorCore/Config.swift b/Sources/_OpenAPIGeneratorCore/Config.swift index e61ff3d3..a13f0446 100644 --- a/Sources/_OpenAPIGeneratorCore/Config.swift +++ b/Sources/_OpenAPIGeneratorCore/Config.swift @@ -28,18 +28,14 @@ public struct Config { /// Additional imports to add to each generated file. public var additionalImports: [String] - /// Where this command has been triggered from. - public var invocationSource: InvocationSource - /// Creates a configuration with the specified generator mode and imports. /// - Parameters: /// - mode: The mode to use for generation. /// - additionalImports: Additional imports to add to each generated /// file. - public init(mode: GeneratorMode, additionalImports: [String] = [], invocationSource: InvocationSource) { + public init(mode: GeneratorMode, additionalImports: [String] = []) { self.mode = mode self.additionalImports = additionalImports - self.invocationSource = invocationSource } } diff --git a/Sources/_OpenAPIGeneratorCore/GeneratorMode.swift b/Sources/_OpenAPIGeneratorCore/GeneratorMode.swift index 2ec2f1be..ff9b0259 100644 --- a/Sources/_OpenAPIGeneratorCore/GeneratorMode.swift +++ b/Sources/_OpenAPIGeneratorCore/GeneratorMode.swift @@ -35,7 +35,9 @@ public enum GeneratorMode: String, Codable, CaseIterable { } extension GeneratorMode { - fileprivate var fileNameSuffix: String { + + /// The Swift file name including its file extension. + public var outputFileName: String { switch self { case .types: return "Types.swift" @@ -46,14 +48,9 @@ extension GeneratorMode { } } - /// The Swift file name including its file extension. - public func fileName(for invocationSource: InvocationSource) -> String { - switch invocationSource { - case .BuildToolPlugin, .CLI: - return self.fileNameSuffix - case .CommandPlugin: - return "Generated_\(self.fileNameSuffix)" - } + /// The Swift file names for all supported generator mode values. + public static var allOutputFileNames: [String] { + GeneratorMode.allCases.map(\.outputFileName) } /// Defines an order in which generators should be run. @@ -69,12 +66,6 @@ extension GeneratorMode { } } -extension Array where Element == GeneratorMode { - public var outputFileNameSuffixes: [String] { - self.map(\.fileNameSuffix) - } -} - extension GeneratorMode: Comparable { public static func < (lhs: GeneratorMode, rhs: GeneratorMode) -> Bool { lhs.order < rhs.order diff --git a/Sources/_OpenAPIGeneratorCore/Translator/ClientTranslator/ClientTranslator.swift b/Sources/_OpenAPIGeneratorCore/Translator/ClientTranslator/ClientTranslator.swift index 5f3705cd..49abd18b 100644 --- a/Sources/_OpenAPIGeneratorCore/Translator/ClientTranslator/ClientTranslator.swift +++ b/Sources/_OpenAPIGeneratorCore/Translator/ClientTranslator/ClientTranslator.swift @@ -148,7 +148,7 @@ struct ClientFileTranslator: FileTranslator { return StructuredSwiftRepresentation( file: .init( - name: GeneratorMode.client.fileName(for: config.invocationSource), + name: GeneratorMode.client.outputFileName, contents: .init( topComment: topComment, imports: imports, diff --git a/Sources/_OpenAPIGeneratorCore/Translator/ServerTranslator/ServerTranslator.swift b/Sources/_OpenAPIGeneratorCore/Translator/ServerTranslator/ServerTranslator.swift index 11e4c11a..f1360371 100644 --- a/Sources/_OpenAPIGeneratorCore/Translator/ServerTranslator/ServerTranslator.swift +++ b/Sources/_OpenAPIGeneratorCore/Translator/ServerTranslator/ServerTranslator.swift @@ -128,7 +128,7 @@ struct ServerFileTranslator: FileTranslator { return StructuredSwiftRepresentation( file: .init( - name: GeneratorMode.server.fileName(for: config.invocationSource), + name: GeneratorMode.server.outputFileName, contents: .init( topComment: topComment, imports: imports, diff --git a/Sources/_OpenAPIGeneratorCore/Translator/TypesTranslator/TypesFileTranslator.swift b/Sources/_OpenAPIGeneratorCore/Translator/TypesTranslator/TypesFileTranslator.swift index 618f8902..703115b1 100644 --- a/Sources/_OpenAPIGeneratorCore/Translator/TypesTranslator/TypesFileTranslator.swift +++ b/Sources/_OpenAPIGeneratorCore/Translator/TypesTranslator/TypesFileTranslator.swift @@ -65,7 +65,7 @@ struct TypesFileTranslator: FileTranslator { return StructuredSwiftRepresentation( file: .init( - name: GeneratorMode.types.fileName(for: config.invocationSource), + name: GeneratorMode.types.outputFileName, contents: typesFile ) ) diff --git a/Sources/swift-openapi-generator/GenerateCommand.swift b/Sources/swift-openapi-generator/GenerateCommand.swift index 30cc2f70..1bcf3c64 100644 --- a/Sources/swift-openapi-generator/GenerateCommand.swift +++ b/Sources/swift-openapi-generator/GenerateCommand.swift @@ -38,7 +38,7 @@ struct _GenerateCommand: AsyncParsableCommand { @Option( help: - "Output directory where the generated files are written. Warning: Replaces any existing files with the same filename. Reserved filename suffixes: \(GeneratorMode.allCases.outputFileNameSuffixes.joined(separator: ", "))" + "Output directory where the generated files are written. Warning: Replaces any existing files with the same filename. Reserved filenames: \(GeneratorMode.allOutputFileNames.joined(separator: ", "))" ) var outputDirectory: URL = URL(fileURLWithPath: FileManager.default.currentDirectoryPath) diff --git a/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift b/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift index 1534354a..b327b1e0 100644 --- a/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift +++ b/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift @@ -54,8 +54,7 @@ extension _GenerateOptions { let configs: [Config] = sortedModes.map { .init( mode: $0, - additionalImports: resolvedAdditionalImports, - invocationSource: invocationSource + additionalImports: resolvedAdditionalImports ) } let diagnostics: DiagnosticCollector @@ -76,7 +75,7 @@ extension _GenerateOptions { - OpenAPI document path: \(doc.path) - Configuration path: \(self.config?.path ?? "") - Generator modes: \(sortedModes.map(\.rawValue).joined(separator: ", ")) - - Output file name suffixes: \(sortedModes.outputFileNameSuffixes.joined(separator: ", ")) + - Output file names: \(sortedModes.map(\.outputFileName).joined(separator: ", ")) - Output directory: \(outputDirectory.path) - Diagnostics output path: \(diagnosticsOutputPath?.path ?? "") - Current directory: \(FileManager.default.currentDirectoryPath) diff --git a/Sources/_OpenAPIGeneratorCore/InvocationSource.swift b/Sources/swift-openapi-generator/InvocationSource.swift similarity index 57% rename from Sources/_OpenAPIGeneratorCore/InvocationSource.swift rename to Sources/swift-openapi-generator/InvocationSource.swift index f51f37a4..6614ec7b 100644 --- a/Sources/_OpenAPIGeneratorCore/InvocationSource.swift +++ b/Sources/swift-openapi-generator/InvocationSource.swift @@ -1,5 +1,5 @@ -public enum InvocationSource: String, Codable { +enum InvocationSource: String, Codable { case BuildToolPlugin case CommandPlugin case CLI diff --git a/Sources/swift-openapi-generator/runGenerator.swift b/Sources/swift-openapi-generator/runGenerator.swift index 0c3c4096..90160540 100644 --- a/Sources/swift-openapi-generator/runGenerator.swift +++ b/Sources/swift-openapi-generator/runGenerator.swift @@ -49,7 +49,7 @@ extension _Tool { docData: docData, config: config, outputDirectory: outputDirectory, - outputFileName: config.mode.fileName(for: invocationSource), + outputFileName: config.mode.outputFileName, diagnostics: diagnostics ) } @@ -61,7 +61,7 @@ extension _Tool { for mode in nonGeneratedModes.sorted() { try replaceFileContents( inDirectory: outputDirectory, - fileName: mode.fileName(for: .BuildToolPlugin), + fileName: mode.outputFileName, with: { Data() } ) } @@ -138,7 +138,7 @@ extension _Tool { static func runBuildToolPluginCleanup(outputDirectory: URL) throws { for mode in GeneratorMode.allCases { - let fileName = mode.fileName(for: .BuildToolPlugin) + let fileName = mode.outputFileName // Swift expects us to always create these files, so we create them but empty. try replaceFileContents( inDirectory: outputDirectory, @@ -156,7 +156,7 @@ extension _Tool { // Remove each file for mode in GeneratorMode.allCases { - let fileName = mode.fileName(for: .CommandPlugin) + let fileName = mode.outputFileName let path = outputDirectory.appendingPathComponent(fileName) if fm.fileExists(atPath: path.path) { try fm.removeItem(at: path) diff --git a/Tests/OpenAPIGeneratorCoreTests/Parser/Test_YamsParser.swift b/Tests/OpenAPIGeneratorCoreTests/Parser/Test_YamsParser.swift index 90a785e8..18d9f52b 100644 --- a/Tests/OpenAPIGeneratorCoreTests/Parser/Test_YamsParser.swift +++ b/Tests/OpenAPIGeneratorCoreTests/Parser/Test_YamsParser.swift @@ -130,7 +130,7 @@ final class Test_YamsParser: Test_Core { absolutePath: URL(fileURLWithPath: "/foo.yaml"), contents: Data(yaml.utf8) ), - config: .init(mode: .types, invocationSource: .BuildToolPlugin), + config: .init(mode: .types), diagnostics: PrintingDiagnosticCollector() ) } diff --git a/Tests/OpenAPIGeneratorCoreTests/TestUtilities.swift b/Tests/OpenAPIGeneratorCoreTests/TestUtilities.swift index 2cc4b3d1..bc4b5076 100644 --- a/Tests/OpenAPIGeneratorCoreTests/TestUtilities.swift +++ b/Tests/OpenAPIGeneratorCoreTests/TestUtilities.swift @@ -25,23 +25,20 @@ class Test_Core: XCTestCase { func makeTranslator( components: OpenAPI.Components = .noComponents, - diagnostics: DiagnosticCollector = PrintingDiagnosticCollector(), - invocationSource: InvocationSource = .BuildToolPlugin + diagnostics: DiagnosticCollector = PrintingDiagnosticCollector() ) -> FileTranslator { makeTypesTranslator( components: components, - diagnostics: diagnostics, - invocationSource: invocationSource + diagnostics: diagnostics ) } func makeTypesTranslator( components: OpenAPI.Components = .noComponents, - diagnostics: DiagnosticCollector = PrintingDiagnosticCollector(), - invocationSource: InvocationSource = .BuildToolPlugin + diagnostics: DiagnosticCollector = PrintingDiagnosticCollector() ) -> TypesFileTranslator { TypesFileTranslator( - config: .init(mode: .types, invocationSource: invocationSource), + config: .init(mode: .types), diagnostics: diagnostics, components: components ) diff --git a/Tests/OpenAPIGeneratorCoreTests/Translator/TypeAssignment/Test_isSchemaSupported.swift b/Tests/OpenAPIGeneratorCoreTests/Translator/TypeAssignment/Test_isSchemaSupported.swift index f8bcd49f..10c77b4c 100644 --- a/Tests/OpenAPIGeneratorCoreTests/Translator/TypeAssignment/Test_isSchemaSupported.swift +++ b/Tests/OpenAPIGeneratorCoreTests/Translator/TypeAssignment/Test_isSchemaSupported.swift @@ -19,7 +19,7 @@ class Test_isSchemaSupported: XCTestCase { var translator: FileTranslator { TypesFileTranslator( - config: .init(mode: .types, invocationSource: .BuildToolPlugin), + config: .init(mode: .types), diagnostics: PrintingDiagnosticCollector(), components: .init(schemas: [ "Foo": .string, diff --git a/Tests/OpenAPIGeneratorReferenceTests/ReferenceTest.swift b/Tests/OpenAPIGeneratorReferenceTests/ReferenceTest.swift index 443750f7..9927dd08 100644 --- a/Tests/OpenAPIGeneratorReferenceTests/ReferenceTest.swift +++ b/Tests/OpenAPIGeneratorReferenceTests/ReferenceTest.swift @@ -20,7 +20,6 @@ struct TestConfig: Encodable { var docFilePath: String var mode: GeneratorMode var additionalImports: [String]? - var invocationSource: InvocationSource var referenceOutputDirectory: String } @@ -28,8 +27,7 @@ extension TestConfig { var asConfig: Config { .init( mode: mode, - additionalImports: additionalImports ?? [], - invocationSource: invocationSource + additionalImports: additionalImports ?? [] ) } } @@ -138,7 +136,6 @@ class ReferenceTests: XCTestCase { docFilePath: "Docs/\(name.fileName)", mode: mode, additionalImports: [], - invocationSource: .BuildToolPlugin, referenceOutputDirectory: "ReferenceSources/\(name.directoryName)" ) ) From c381a0ccab8788f807ec3487ecf32956d25e56ee Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Tue, 4 Jul 2023 03:26:45 +0330 Subject: [PATCH 025/100] better message when running the generator --- .../swift-openapi-generator/GenerateOptions+runGenerator.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift b/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift index b327b1e0..6c86d822 100644 --- a/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift +++ b/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift @@ -79,7 +79,7 @@ extension _GenerateOptions { - Output directory: \(outputDirectory.path) - Diagnostics output path: \(diagnosticsOutputPath?.path ?? "") - Current directory: \(FileManager.default.currentDirectoryPath) - - Invocation kind: \(invocationSource.rawValue) + - Invoked from: \(invocationSource) - Additional imports: \(resolvedAdditionalImports.isEmpty ? "" : resolvedAdditionalImports.joined(separator: ", ")) """ ) From e2c085c0ab0e09fcceb25a3a378d1e93c64981cd Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Tue, 4 Jul 2023 03:33:52 +0330 Subject: [PATCH 026/100] cleanup --- Plugins/PluginsShared/PluginUtils.swift | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Plugins/PluginsShared/PluginUtils.swift b/Plugins/PluginsShared/PluginUtils.swift index 25fe0e73..4dda98a8 100644 --- a/Plugins/PluginsShared/PluginUtils.swift +++ b/Plugins/PluginsShared/PluginUtils.swift @@ -48,16 +48,12 @@ enum PluginUtils { let tool = try tool("swift-openapi-generator") - let inputs = ValidatedInputs( + return ValidatedInputs( doc: doc, config: config, genSourcesDir: genSourcesDir, arguments: arguments, tool: tool ) - - print("INPUTS for \(invocationSource)", inputs) - - return inputs } } From ff25cba3434d952a649d63debbbbfd4c28e771fe Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Tue, 4 Jul 2023 03:37:23 +0330 Subject: [PATCH 027/100] fix passing bad arguments --- Plugins/PluginsShared/PluginUtils.swift | 2 +- .../swift-openapi-generator/GenerateOptions+runGenerator.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Plugins/PluginsShared/PluginUtils.swift b/Plugins/PluginsShared/PluginUtils.swift index 4dda98a8..5f5a1b0c 100644 --- a/Plugins/PluginsShared/PluginUtils.swift +++ b/Plugins/PluginsShared/PluginUtils.swift @@ -43,7 +43,7 @@ enum PluginUtils { "generate", "\(doc)", "--config", "\(config)", "--output-directory", "\(genSourcesDir)", - "--invoked-from", "\(invocationSource)" + "--invoked-from", "\(invocationSource.rawValue)" ] let tool = try tool("swift-openapi-generator") diff --git a/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift b/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift index 6c86d822..c894df6f 100644 --- a/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift +++ b/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift @@ -79,7 +79,7 @@ extension _GenerateOptions { - Output directory: \(outputDirectory.path) - Diagnostics output path: \(diagnosticsOutputPath?.path ?? "") - Current directory: \(FileManager.default.currentDirectoryPath) - - Invoked from: \(invocationSource) + - Invoked from: \(invocationSource.rawValue) - Additional imports: \(resolvedAdditionalImports.isEmpty ? "" : resolvedAdditionalImports.joined(separator: ", ")) """ ) From ceaeb2dae9c73cbaafdcd701e61cf9179015aa45 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Tue, 4 Jul 2023 03:41:01 +0330 Subject: [PATCH 028/100] even more removing unneeded complexity --- .../GenerateOptions+runGenerator.swift | 17 --------- .../swift-openapi-generator/UserConfig.swift | 9 +---- .../runGenerator.swift | 37 ------------------- 3 files changed, 1 insertion(+), 62 deletions(-) diff --git a/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift b/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift index c894df6f..43acddaf 100644 --- a/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift +++ b/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift @@ -32,23 +32,6 @@ extension _GenerateOptions { invocationSource: InvocationSource ) throws { let config = try loadedConfig() - - switch invocationSource { - case .BuildToolPlugin: - guard (config?.pluginMode ?? .BuildTool) == .BuildTool else { - print("Plugin disabled for BuildTool plugins. Will clean up files if there are any leftovers from previous builds.") - try _Tool.runBuildToolPluginCleanup(outputDirectory: outputDirectory) - return - } - case .CommandPlugin: - guard (config?.pluginMode ?? .BuildTool) == .Command else { - print("Plugin disabled for Command plugins. Will clean up files if there are any leftovers from previous builds.") - try _Tool.runCommandPluginCleanup(outputDirectory: outputDirectory) - return - } - default: break - } - let sortedModes = try resolvedModes(config) let resolvedAdditionalImports = resolvedAdditionalImports(config) let configs: [Config] = sortedModes.map { diff --git a/Sources/swift-openapi-generator/UserConfig.swift b/Sources/swift-openapi-generator/UserConfig.swift index 94c0845e..db198f6f 100644 --- a/Sources/swift-openapi-generator/UserConfig.swift +++ b/Sources/swift-openapi-generator/UserConfig.swift @@ -19,18 +19,11 @@ import _OpenAPIGeneratorCore /// of the generator pipeline, and, for example, generate both Types.swift and /// Client.swift in one invocation of the command-line tool. struct _UserConfig: Codable { - - enum PluginMode: String, Codable { - case BuildTool - case Command - } - + /// A list of modes to use, in other words, which Swift files to generate. var generate: [GeneratorMode] /// A list of names of additional imports that are added to every /// generated Swift file. var additionalImports: [String]? - - var pluginMode: PluginMode? } diff --git a/Sources/swift-openapi-generator/runGenerator.swift b/Sources/swift-openapi-generator/runGenerator.swift index 90160540..cd6d7ece 100644 --- a/Sources/swift-openapi-generator/runGenerator.swift +++ b/Sources/swift-openapi-generator/runGenerator.swift @@ -135,41 +135,4 @@ extension _Tool { return fm.createFile(atPath: path.path, contents: data) } } - - static func runBuildToolPluginCleanup(outputDirectory: URL) throws { - for mode in GeneratorMode.allCases { - let fileName = mode.outputFileName - // Swift expects us to always create these files, so we create them but empty. - try replaceFileContents( - inDirectory: outputDirectory, - fileName: fileName, - with: { Data() } - ) - } - } - - static func runCommandPluginCleanup(outputDirectory: URL) throws { - let fm = FileManager.default - guard fm.fileExists(atPath: outputDirectory.path) else { - return - } - - // Remove each file - for mode in GeneratorMode.allCases { - let fileName = mode.outputFileName - let path = outputDirectory.appendingPathComponent(fileName) - if fm.fileExists(atPath: path.path) { - try fm.removeItem(at: path) - } - } - - // If the output directory is empty, remove it. - let outputDirectoryContents = try? fm.contentsOfDirectory( - at: outputDirectory, - includingPropertiesForKeys: nil - ) - if (outputDirectoryContents ?? []).isEmpty { - try fm.removeItem(at: outputDirectory) - } - } } From 98877428a958cc150f011a881e7408b49196af96 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Tue, 4 Jul 2023 04:11:53 +0330 Subject: [PATCH 029/100] refinements --- Sources/swift-openapi-generator/runGenerator.swift | 2 +- .../CommonTranslations/Test_translateStructBlueprint.swift | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Sources/swift-openapi-generator/runGenerator.swift b/Sources/swift-openapi-generator/runGenerator.swift index cd6d7ece..b887dd61 100644 --- a/Sources/swift-openapi-generator/runGenerator.swift +++ b/Sources/swift-openapi-generator/runGenerator.swift @@ -95,7 +95,7 @@ extension _Tool { ) return output.contents } - print("File with name '\(outputFileName)' in directory '\(outputDirectory.path)': \(didChange ? "changed" : "unchanged")") + print("File \(outputFileName): \(didChange ? "changed" : "unchanged")") } /// Evaluates a closure to generate file data and writes the data to disk diff --git a/Tests/OpenAPIGeneratorCoreTests/Translator/CommonTranslations/Test_translateStructBlueprint.swift b/Tests/OpenAPIGeneratorCoreTests/Translator/CommonTranslations/Test_translateStructBlueprint.swift index bad0a68c..4c642342 100644 --- a/Tests/OpenAPIGeneratorCoreTests/Translator/CommonTranslations/Test_translateStructBlueprint.swift +++ b/Tests/OpenAPIGeneratorCoreTests/Translator/CommonTranslations/Test_translateStructBlueprint.swift @@ -55,8 +55,7 @@ final class Test_translateStructBlueprint: Test_Core { func testDeprecatedStruct() throws { let blueprint = StructBlueprint(isDeprecated: true, typeName: Self.testTypeName, properties: []) - let decl = makeTypesTranslator() - .translateStructBlueprint(blueprint) + let decl = makeTypesTranslator().translateStructBlueprint(blueprint) XCTAssertEqual(decl.strippingTopComment.info.kind, .deprecated) } From 8bd7363cf20c0699b95a1026925ad3c2ae6986e3 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Tue, 4 Jul 2023 07:10:18 +0330 Subject: [PATCH 030/100] Use existential any --- .../StructuredSwiftRepresentation.swift | 5 +++ .../Renderer/TextBasedRenderer.swift | 3 ++ .../ClientTranslator/ClientTranslator.swift | 8 +++- .../CommonTranslations/translateCodable.swift | 4 +- .../ServerTranslator/ServerTranslator.swift | 3 +- .../ReferenceSources/Petstore/Client.swift | 4 +- .../ReferenceSources/Petstore/Server.swift | 4 +- .../ReferenceSources/Petstore/Types.swift | 38 ++++++++++--------- 8 files changed, 42 insertions(+), 27 deletions(-) diff --git a/Sources/_OpenAPIGeneratorCore/Layers/StructuredSwiftRepresentation.swift b/Sources/_OpenAPIGeneratorCore/Layers/StructuredSwiftRepresentation.swift index 29989ae3..72d1ed8d 100644 --- a/Sources/_OpenAPIGeneratorCore/Layers/StructuredSwiftRepresentation.swift +++ b/Sources/_OpenAPIGeneratorCore/Layers/StructuredSwiftRepresentation.swift @@ -377,6 +377,11 @@ struct ParameterDescription: Equatable, Codable { /// For example, in `bar baz: String = "hi"`, `name` is `baz`. var name: String? = nil + /// The existential any keyword. + /// + /// For example, in `bar baz: any Client = ...`, `any` is the keyword referred to here. + var anyKeyword: Bool = false + /// The type name of the parameter. /// /// For example, in `bar baz: String = "hi"`, `type` is `String`. diff --git a/Sources/_OpenAPIGeneratorCore/Renderer/TextBasedRenderer.swift b/Sources/_OpenAPIGeneratorCore/Renderer/TextBasedRenderer.swift index 4985ebb9..33e26861 100644 --- a/Sources/_OpenAPIGeneratorCore/Renderer/TextBasedRenderer.swift +++ b/Sources/_OpenAPIGeneratorCore/Renderer/TextBasedRenderer.swift @@ -610,6 +610,9 @@ struct TextBasedRenderer: RendererProtocol { } } words.append(":") + if parameterDescription.anyKeyword { + words.append("any") + } words.append(parameterDescription.type) if let defaultValue = parameterDescription.defaultValue { words.append("=") diff --git a/Sources/_OpenAPIGeneratorCore/Translator/ClientTranslator/ClientTranslator.swift b/Sources/_OpenAPIGeneratorCore/Translator/ClientTranslator/ClientTranslator.swift index 49abd18b..d1b94b85 100644 --- a/Sources/_OpenAPIGeneratorCore/Translator/ClientTranslator/ClientTranslator.swift +++ b/Sources/_OpenAPIGeneratorCore/Translator/ClientTranslator/ClientTranslator.swift @@ -79,10 +79,14 @@ struct ClientFileTranslator: FileTranslator { type: Constants.Configuration.typeName, defaultValue: .dot("init").call([]) ), - .init(label: "transport", type: Constants.Client.Transport.typeName), + .init( + label: "transport", + anyKeyword: true, + type: Constants.Client.Transport.typeName + ), .init( label: "middlewares", - type: "[\(Constants.Client.Middleware.typeName)]", + type: "[any \(Constants.Client.Middleware.typeName)]", defaultValue: .literal(.array([])) ), ], diff --git a/Sources/_OpenAPIGeneratorCore/Translator/CommonTranslations/translateCodable.swift b/Sources/_OpenAPIGeneratorCore/Translator/CommonTranslations/translateCodable.swift index cba769d4..5477034d 100644 --- a/Sources/_OpenAPIGeneratorCore/Translator/CommonTranslations/translateCodable.swift +++ b/Sources/_OpenAPIGeneratorCore/Translator/CommonTranslations/translateCodable.swift @@ -593,7 +593,7 @@ fileprivate extension FileTranslator { accessModifier: config.access, kind: .function(name: "encode"), parameters: [ - .init(label: "to", name: "encoder", type: "Encoder") + .init(label: "to", name: "encoder", anyKeyword: true, type: "Encoder") ], keywords: [ .throws @@ -610,7 +610,7 @@ fileprivate extension FileTranslator { accessModifier: config.access, kind: .initializer, parameters: [ - .init(label: "from", name: "decoder", type: "Decoder") + .init(label: "from", name: "decoder", anyKeyword: true, type: "Decoder") ], keywords: [ .throws diff --git a/Sources/_OpenAPIGeneratorCore/Translator/ServerTranslator/ServerTranslator.swift b/Sources/_OpenAPIGeneratorCore/Translator/ServerTranslator/ServerTranslator.swift index f1360371..15a33618 100644 --- a/Sources/_OpenAPIGeneratorCore/Translator/ServerTranslator/ServerTranslator.swift +++ b/Sources/_OpenAPIGeneratorCore/Translator/ServerTranslator/ServerTranslator.swift @@ -79,6 +79,7 @@ struct ServerFileTranslator: FileTranslator { .init( label: "on", name: "transport", + anyKeyword: true, type: Constants.Server.Transport.typeName ), .init( @@ -93,7 +94,7 @@ struct ServerFileTranslator: FileTranslator { ), .init( label: "middlewares", - type: "[\(Constants.Server.Middleware.typeName)]", + type: "[any \(Constants.Server.Middleware.typeName)]", defaultValue: .literal(.array([])) ), ], diff --git a/Tests/OpenAPIGeneratorReferenceTests/Resources/ReferenceSources/Petstore/Client.swift b/Tests/OpenAPIGeneratorReferenceTests/Resources/ReferenceSources/Petstore/Client.swift index eec517b0..d8cd6c51 100644 --- a/Tests/OpenAPIGeneratorReferenceTests/Resources/ReferenceSources/Petstore/Client.swift +++ b/Tests/OpenAPIGeneratorReferenceTests/Resources/ReferenceSources/Petstore/Client.swift @@ -22,8 +22,8 @@ public struct Client: APIProtocol { public init( serverURL: URL, configuration: Configuration = .init(), - transport: ClientTransport, - middlewares: [ClientMiddleware] = [] + transport: any ClientTransport, + middlewares: [any ClientMiddleware] = [] ) { self.client = .init( serverURL: serverURL, diff --git a/Tests/OpenAPIGeneratorReferenceTests/Resources/ReferenceSources/Petstore/Server.swift b/Tests/OpenAPIGeneratorReferenceTests/Resources/ReferenceSources/Petstore/Server.swift index 88155753..65bb9c9c 100644 --- a/Tests/OpenAPIGeneratorReferenceTests/Resources/ReferenceSources/Petstore/Server.swift +++ b/Tests/OpenAPIGeneratorReferenceTests/Resources/ReferenceSources/Petstore/Server.swift @@ -14,10 +14,10 @@ extension APIProtocol { /// - configuration: A set of configuration values for the server. /// - middlewares: A list of middlewares to call before the handler. public func registerHandlers( - on transport: ServerTransport, + on transport: any ServerTransport, serverURL: URL = .defaultOpenAPIServerURL, configuration: Configuration = .init(), - middlewares: [ServerMiddleware] = [] + middlewares: [any ServerMiddleware] = [] ) throws { let server = UniversalServer( serverURL: serverURL, diff --git a/Tests/OpenAPIGeneratorReferenceTests/Resources/ReferenceSources/Petstore/Types.swift b/Tests/OpenAPIGeneratorReferenceTests/Resources/ReferenceSources/Petstore/Types.swift index a510aad7..1323ed61 100644 --- a/Tests/OpenAPIGeneratorReferenceTests/Resources/ReferenceSources/Petstore/Types.swift +++ b/Tests/OpenAPIGeneratorReferenceTests/Resources/ReferenceSources/Petstore/Types.swift @@ -177,8 +177,10 @@ public enum Components { /// - Parameters: /// - value1: public init(value1: Components.Schemas.ExtraInfo) { self.value1 = value1 } - public init(from decoder: Decoder) throws { value1 = try .init(from: decoder) } - public func encode(to encoder: Encoder) throws { try value1.encode(to: encoder) } + public init(from decoder: any Decoder) throws { value1 = try .init(from: decoder) } + public func encode(to encoder: any Encoder) throws { + try value1.encode(to: encoder) + } } /// Extra information about the error. /// @@ -268,7 +270,7 @@ public enum Components { /// - foo: public init(foo: Swift.String? = nil) { self.foo = foo } public enum CodingKeys: String, CodingKey { case foo } - public init(from decoder: Decoder) throws { + public init(from decoder: any Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) foo = try container.decodeIfPresent(Swift.String.self, forKey: .foo) try decoder.ensureNoAdditionalProperties(knownKeys: ["foo"]) @@ -293,12 +295,12 @@ public enum Components { self.additionalProperties = additionalProperties } public enum CodingKeys: String, CodingKey { case foo } - public init(from decoder: Decoder) throws { + public init(from decoder: any Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) foo = try container.decodeIfPresent(Swift.String.self, forKey: .foo) additionalProperties = try decoder.decodeAdditionalProperties(knownKeys: ["foo"]) } - public func encode(to encoder: Encoder) throws { + public func encode(to encoder: any Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encodeIfPresent(foo, forKey: .foo) try encoder.encodeAdditionalProperties(additionalProperties) @@ -323,12 +325,12 @@ public enum Components { self.additionalProperties = additionalProperties } public enum CodingKeys: String, CodingKey { case foo } - public init(from decoder: Decoder) throws { + public init(from decoder: any Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) foo = try container.decodeIfPresent(Swift.String.self, forKey: .foo) additionalProperties = try decoder.decodeAdditionalProperties(knownKeys: ["foo"]) } - public func encode(to encoder: Encoder) throws { + public func encode(to encoder: any Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encodeIfPresent(foo, forKey: .foo) try encoder.encodeAdditionalProperties(additionalProperties) @@ -374,11 +376,11 @@ public enum Components { self.value1 = value1 self.value2 = value2 } - public init(from decoder: Decoder) throws { + public init(from decoder: any Decoder) throws { value1 = try .init(from: decoder) value2 = try .init(from: decoder) } - public func encode(to encoder: Encoder) throws { + public func encode(to encoder: any Encoder) throws { try value1.encode(to: encoder) try value2.encode(to: encoder) } @@ -412,7 +414,7 @@ public enum Components { self.value1 = value1 self.value2 = value2 } - public init(from decoder: Decoder) throws { + public init(from decoder: any Decoder) throws { value1 = try? .init(from: decoder) value2 = try? .init(from: decoder) try DecodingError.verifyAtLeastOneSchemaIsNotNil( @@ -421,7 +423,7 @@ public enum Components { codingPath: decoder.codingPath ) } - public func encode(to encoder: Encoder) throws { + public func encode(to encoder: any Encoder) throws { try value1?.encode(to: encoder) try value2?.encode(to: encoder) } @@ -449,7 +451,7 @@ public enum Components { case case4(Components.Schemas.OneOfAny.Case4Payload) /// Parsed a case that was not defined in the OpenAPI document. case undocumented(OpenAPIRuntime.OpenAPIValueContainer) - public init(from decoder: Decoder) throws { + public init(from decoder: any Decoder) throws { do { self = .case1(try .init(from: decoder)) return @@ -470,7 +472,7 @@ public enum Components { let value = try container.decode(OpenAPIRuntime.OpenAPIValueContainer.self) self = .undocumented(value) } - public func encode(to encoder: Encoder) throws { + public func encode(to encoder: any Encoder) throws { switch self { case let .case1(value): try value.encode(to: encoder) case let .case2(value): try value.encode(to: encoder) @@ -540,11 +542,11 @@ public enum Components { self.value1 = value1 self.value2 = value2 } - public init(from decoder: Decoder) throws { + public init(from decoder: any Decoder) throws { value1 = try .init(from: decoder) value2 = try .init(from: decoder) } - public func encode(to encoder: Encoder) throws { + public func encode(to encoder: any Encoder) throws { try value1.encode(to: encoder) try value2.encode(to: encoder) } @@ -558,7 +560,7 @@ public enum Components { /// Parsed a case that was not defined in the OpenAPI document. case undocumented(OpenAPIRuntime.OpenAPIObjectContainer) public enum CodingKeys: String, CodingKey { case kind } - public init(from decoder: Decoder) throws { + public init(from decoder: any Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) let discriminator = try container.decode(String.self, forKey: .kind) switch discriminator { @@ -570,7 +572,7 @@ public enum Components { self = .undocumented(value) } } - public func encode(to encoder: Encoder) throws { + public func encode(to encoder: any Encoder) throws { switch self { case let .Walk(value): try value.encode(to: encoder) case let .MessagedExercise(value): try value.encode(to: encoder) @@ -583,7 +585,7 @@ public enum Components { public struct DeprecatedObject: Codable, Equatable, Hashable, Sendable { /// Creates a new `DeprecatedObject`. public init() {} - public init(from decoder: Decoder) throws { + public init(from decoder: any Decoder) throws { try decoder.ensureNoAdditionalProperties(knownKeys: []) } } From e4340215e73c82c97a96f8c9430b8475e60b9bed Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Tue, 11 Jul 2023 13:09:54 +0330 Subject: [PATCH 031/100] move "any"s to the type names --- .../Layers/StructuredSwiftRepresentation.swift | 5 ----- .../Renderer/TextBasedRenderer.swift | 3 --- .../Translator/ClientTranslator/ClientTranslator.swift | 3 +-- .../Translator/CommonTranslations/translateCodable.swift | 4 ++-- .../Translator/CommonTypes/Constants.swift | 8 ++++---- .../Translator/ServerTranslator/ServerTranslator.swift | 3 +-- 6 files changed, 8 insertions(+), 18 deletions(-) diff --git a/Sources/_OpenAPIGeneratorCore/Layers/StructuredSwiftRepresentation.swift b/Sources/_OpenAPIGeneratorCore/Layers/StructuredSwiftRepresentation.swift index 72d1ed8d..29989ae3 100644 --- a/Sources/_OpenAPIGeneratorCore/Layers/StructuredSwiftRepresentation.swift +++ b/Sources/_OpenAPIGeneratorCore/Layers/StructuredSwiftRepresentation.swift @@ -377,11 +377,6 @@ struct ParameterDescription: Equatable, Codable { /// For example, in `bar baz: String = "hi"`, `name` is `baz`. var name: String? = nil - /// The existential any keyword. - /// - /// For example, in `bar baz: any Client = ...`, `any` is the keyword referred to here. - var anyKeyword: Bool = false - /// The type name of the parameter. /// /// For example, in `bar baz: String = "hi"`, `type` is `String`. diff --git a/Sources/_OpenAPIGeneratorCore/Renderer/TextBasedRenderer.swift b/Sources/_OpenAPIGeneratorCore/Renderer/TextBasedRenderer.swift index 33e26861..4985ebb9 100644 --- a/Sources/_OpenAPIGeneratorCore/Renderer/TextBasedRenderer.swift +++ b/Sources/_OpenAPIGeneratorCore/Renderer/TextBasedRenderer.swift @@ -610,9 +610,6 @@ struct TextBasedRenderer: RendererProtocol { } } words.append(":") - if parameterDescription.anyKeyword { - words.append("any") - } words.append(parameterDescription.type) if let defaultValue = parameterDescription.defaultValue { words.append("=") diff --git a/Sources/_OpenAPIGeneratorCore/Translator/ClientTranslator/ClientTranslator.swift b/Sources/_OpenAPIGeneratorCore/Translator/ClientTranslator/ClientTranslator.swift index d1b94b85..1f7ecf8f 100644 --- a/Sources/_OpenAPIGeneratorCore/Translator/ClientTranslator/ClientTranslator.swift +++ b/Sources/_OpenAPIGeneratorCore/Translator/ClientTranslator/ClientTranslator.swift @@ -81,12 +81,11 @@ struct ClientFileTranslator: FileTranslator { ), .init( label: "transport", - anyKeyword: true, type: Constants.Client.Transport.typeName ), .init( label: "middlewares", - type: "[any \(Constants.Client.Middleware.typeName)]", + type: "[\(Constants.Client.Middleware.typeName)]", defaultValue: .literal(.array([])) ), ], diff --git a/Sources/_OpenAPIGeneratorCore/Translator/CommonTranslations/translateCodable.swift b/Sources/_OpenAPIGeneratorCore/Translator/CommonTranslations/translateCodable.swift index 5477034d..47247f08 100644 --- a/Sources/_OpenAPIGeneratorCore/Translator/CommonTranslations/translateCodable.swift +++ b/Sources/_OpenAPIGeneratorCore/Translator/CommonTranslations/translateCodable.swift @@ -593,7 +593,7 @@ fileprivate extension FileTranslator { accessModifier: config.access, kind: .function(name: "encode"), parameters: [ - .init(label: "to", name: "encoder", anyKeyword: true, type: "Encoder") + .init(label: "to", name: "encoder", type: "any Encoder") ], keywords: [ .throws @@ -610,7 +610,7 @@ fileprivate extension FileTranslator { accessModifier: config.access, kind: .initializer, parameters: [ - .init(label: "from", name: "decoder", anyKeyword: true, type: "Decoder") + .init(label: "from", name: "decoder", type: "any Decoder") ], keywords: [ .throws diff --git a/Sources/_OpenAPIGeneratorCore/Translator/CommonTypes/Constants.swift b/Sources/_OpenAPIGeneratorCore/Translator/CommonTypes/Constants.swift index 042ac6ff..95b8d097 100644 --- a/Sources/_OpenAPIGeneratorCore/Translator/CommonTypes/Constants.swift +++ b/Sources/_OpenAPIGeneratorCore/Translator/CommonTypes/Constants.swift @@ -80,14 +80,14 @@ enum Constants { enum Transport { /// The name of the client transport type. - static let typeName: String = "ClientTransport" + static let typeName: String = "any ClientTransport" } /// Constants related to the client middleware type. enum Middleware { /// The name of the client middleware type. - static let typeName: String = "ClientMiddleware" + static let typeName: String = "any ClientMiddleware" } } @@ -110,14 +110,14 @@ enum Constants { enum Transport { /// The name of the server transport type. - static let typeName: String = "ServerTransport" + static let typeName: String = "any ServerTransport" } /// Constants related to the server middleware type. enum Middleware { /// The name of the server middleware type. - static let typeName: String = "ServerMiddleware" + static let typeName: String = "any ServerMiddleware" } } diff --git a/Sources/_OpenAPIGeneratorCore/Translator/ServerTranslator/ServerTranslator.swift b/Sources/_OpenAPIGeneratorCore/Translator/ServerTranslator/ServerTranslator.swift index 15a33618..f1360371 100644 --- a/Sources/_OpenAPIGeneratorCore/Translator/ServerTranslator/ServerTranslator.swift +++ b/Sources/_OpenAPIGeneratorCore/Translator/ServerTranslator/ServerTranslator.swift @@ -79,7 +79,6 @@ struct ServerFileTranslator: FileTranslator { .init( label: "on", name: "transport", - anyKeyword: true, type: Constants.Server.Transport.typeName ), .init( @@ -94,7 +93,7 @@ struct ServerFileTranslator: FileTranslator { ), .init( label: "middlewares", - type: "[any \(Constants.Server.Middleware.typeName)]", + type: "[\(Constants.Server.Middleware.typeName)]", defaultValue: .literal(.array([])) ), ], From bdcfd57bfc8a317dbd9a9dda8bb38787cf8fd562 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Tue, 11 Jul 2023 13:10:41 +0330 Subject: [PATCH 032/100] Revert "move "any"s to the type names" This reverts commit e4340215e73c82c97a96f8c9430b8475e60b9bed. --- .../Layers/StructuredSwiftRepresentation.swift | 5 +++++ .../Renderer/TextBasedRenderer.swift | 3 +++ .../Translator/ClientTranslator/ClientTranslator.swift | 3 ++- .../Translator/CommonTranslations/translateCodable.swift | 4 ++-- .../Translator/CommonTypes/Constants.swift | 8 ++++---- .../Translator/ServerTranslator/ServerTranslator.swift | 3 ++- 6 files changed, 18 insertions(+), 8 deletions(-) diff --git a/Sources/_OpenAPIGeneratorCore/Layers/StructuredSwiftRepresentation.swift b/Sources/_OpenAPIGeneratorCore/Layers/StructuredSwiftRepresentation.swift index 29989ae3..72d1ed8d 100644 --- a/Sources/_OpenAPIGeneratorCore/Layers/StructuredSwiftRepresentation.swift +++ b/Sources/_OpenAPIGeneratorCore/Layers/StructuredSwiftRepresentation.swift @@ -377,6 +377,11 @@ struct ParameterDescription: Equatable, Codable { /// For example, in `bar baz: String = "hi"`, `name` is `baz`. var name: String? = nil + /// The existential any keyword. + /// + /// For example, in `bar baz: any Client = ...`, `any` is the keyword referred to here. + var anyKeyword: Bool = false + /// The type name of the parameter. /// /// For example, in `bar baz: String = "hi"`, `type` is `String`. diff --git a/Sources/_OpenAPIGeneratorCore/Renderer/TextBasedRenderer.swift b/Sources/_OpenAPIGeneratorCore/Renderer/TextBasedRenderer.swift index 4985ebb9..33e26861 100644 --- a/Sources/_OpenAPIGeneratorCore/Renderer/TextBasedRenderer.swift +++ b/Sources/_OpenAPIGeneratorCore/Renderer/TextBasedRenderer.swift @@ -610,6 +610,9 @@ struct TextBasedRenderer: RendererProtocol { } } words.append(":") + if parameterDescription.anyKeyword { + words.append("any") + } words.append(parameterDescription.type) if let defaultValue = parameterDescription.defaultValue { words.append("=") diff --git a/Sources/_OpenAPIGeneratorCore/Translator/ClientTranslator/ClientTranslator.swift b/Sources/_OpenAPIGeneratorCore/Translator/ClientTranslator/ClientTranslator.swift index 1f7ecf8f..d1b94b85 100644 --- a/Sources/_OpenAPIGeneratorCore/Translator/ClientTranslator/ClientTranslator.swift +++ b/Sources/_OpenAPIGeneratorCore/Translator/ClientTranslator/ClientTranslator.swift @@ -81,11 +81,12 @@ struct ClientFileTranslator: FileTranslator { ), .init( label: "transport", + anyKeyword: true, type: Constants.Client.Transport.typeName ), .init( label: "middlewares", - type: "[\(Constants.Client.Middleware.typeName)]", + type: "[any \(Constants.Client.Middleware.typeName)]", defaultValue: .literal(.array([])) ), ], diff --git a/Sources/_OpenAPIGeneratorCore/Translator/CommonTranslations/translateCodable.swift b/Sources/_OpenAPIGeneratorCore/Translator/CommonTranslations/translateCodable.swift index 47247f08..5477034d 100644 --- a/Sources/_OpenAPIGeneratorCore/Translator/CommonTranslations/translateCodable.swift +++ b/Sources/_OpenAPIGeneratorCore/Translator/CommonTranslations/translateCodable.swift @@ -593,7 +593,7 @@ fileprivate extension FileTranslator { accessModifier: config.access, kind: .function(name: "encode"), parameters: [ - .init(label: "to", name: "encoder", type: "any Encoder") + .init(label: "to", name: "encoder", anyKeyword: true, type: "Encoder") ], keywords: [ .throws @@ -610,7 +610,7 @@ fileprivate extension FileTranslator { accessModifier: config.access, kind: .initializer, parameters: [ - .init(label: "from", name: "decoder", type: "any Decoder") + .init(label: "from", name: "decoder", anyKeyword: true, type: "Decoder") ], keywords: [ .throws diff --git a/Sources/_OpenAPIGeneratorCore/Translator/CommonTypes/Constants.swift b/Sources/_OpenAPIGeneratorCore/Translator/CommonTypes/Constants.swift index 95b8d097..042ac6ff 100644 --- a/Sources/_OpenAPIGeneratorCore/Translator/CommonTypes/Constants.swift +++ b/Sources/_OpenAPIGeneratorCore/Translator/CommonTypes/Constants.swift @@ -80,14 +80,14 @@ enum Constants { enum Transport { /// The name of the client transport type. - static let typeName: String = "any ClientTransport" + static let typeName: String = "ClientTransport" } /// Constants related to the client middleware type. enum Middleware { /// The name of the client middleware type. - static let typeName: String = "any ClientMiddleware" + static let typeName: String = "ClientMiddleware" } } @@ -110,14 +110,14 @@ enum Constants { enum Transport { /// The name of the server transport type. - static let typeName: String = "any ServerTransport" + static let typeName: String = "ServerTransport" } /// Constants related to the server middleware type. enum Middleware { /// The name of the server middleware type. - static let typeName: String = "any ServerMiddleware" + static let typeName: String = "ServerMiddleware" } } diff --git a/Sources/_OpenAPIGeneratorCore/Translator/ServerTranslator/ServerTranslator.swift b/Sources/_OpenAPIGeneratorCore/Translator/ServerTranslator/ServerTranslator.swift index f1360371..15a33618 100644 --- a/Sources/_OpenAPIGeneratorCore/Translator/ServerTranslator/ServerTranslator.swift +++ b/Sources/_OpenAPIGeneratorCore/Translator/ServerTranslator/ServerTranslator.swift @@ -79,6 +79,7 @@ struct ServerFileTranslator: FileTranslator { .init( label: "on", name: "transport", + anyKeyword: true, type: Constants.Server.Transport.typeName ), .init( @@ -93,7 +94,7 @@ struct ServerFileTranslator: FileTranslator { ), .init( label: "middlewares", - type: "[\(Constants.Server.Middleware.typeName)]", + type: "[any \(Constants.Server.Middleware.typeName)]", defaultValue: .literal(.array([])) ), ], From 74c4e0af58a55aa3aa60b376bdb64516f861d461 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Thu, 13 Jul 2023 09:09:49 +0330 Subject: [PATCH 033/100] Update Package.swift Co-authored-by: Honza Dvorsky --- Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index c8a6f65d..52786428 100644 --- a/Package.swift +++ b/Package.swift @@ -34,7 +34,7 @@ let package = Package( products: [ .executable(name: "swift-openapi-generator", targets: ["swift-openapi-generator"]), .plugin(name: "OpenAPIGenerator", targets: ["OpenAPIGenerator"]), - .plugin(name: "OpenAPIGeneratorCommandPlugin", targets: ["OpenAPIGeneratorCommandPlugin"]), + .plugin(name: "OpenAPIGeneratorCommand", targets: ["OpenAPIGeneratorCommand"]), .library(name: "_OpenAPIGeneratorCore", targets: ["_OpenAPIGeneratorCore"]), ], dependencies: [ From d3567d9dadcf1cc4aa617c245276f580a0e12138 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Thu, 13 Jul 2023 09:09:56 +0330 Subject: [PATCH 034/100] Update Package.swift Co-authored-by: Honza Dvorsky --- Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index 52786428..05c036ac 100644 --- a/Package.swift +++ b/Package.swift @@ -156,7 +156,7 @@ let package = Package( verb: "generate-openapi-code", description: "Generates OpenAPI code" ), - permissions: [.writeToPackageDirectory(reason: "To generate OpenAPI code")] + permissions: [.writeToPackageDirectory(reason: "To write the generated Swift files back into the source directory of the package.")] ), dependencies: [ "swift-openapi-generator", From 4a762b02b2ee8941d3685c01d93d0043941a6068 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Thu, 13 Jul 2023 09:14:37 +0330 Subject: [PATCH 035/100] fix build --- Package.swift | 3 ++- .../PluginsShared | 0 .../plugin.swift | 0 .../{OpenAPIGenerator => PluginsShared}/GeneratorMode.swift | 0 4 files changed, 2 insertions(+), 1 deletion(-) rename Plugins/{OpenAPIGeneratorCommandPlugin => OpenAPIGeneratorCommand}/PluginsShared (100%) rename Plugins/{OpenAPIGeneratorCommandPlugin => OpenAPIGeneratorCommand}/plugin.swift (100%) rename Plugins/{OpenAPIGenerator => PluginsShared}/GeneratorMode.swift (100%) diff --git a/Package.swift b/Package.swift index 05c036ac..7dbfe845 100644 --- a/Package.swift +++ b/Package.swift @@ -149,8 +149,9 @@ let package = Package( ] ), + // Command Plugin .plugin( - name: "OpenAPIGeneratorCommandPlugin", + name: "OpenAPIGeneratorCommand", capability: Target.PluginCapability.command( intent: .custom( verb: "generate-openapi-code", diff --git a/Plugins/OpenAPIGeneratorCommandPlugin/PluginsShared b/Plugins/OpenAPIGeneratorCommand/PluginsShared similarity index 100% rename from Plugins/OpenAPIGeneratorCommandPlugin/PluginsShared rename to Plugins/OpenAPIGeneratorCommand/PluginsShared diff --git a/Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift b/Plugins/OpenAPIGeneratorCommand/plugin.swift similarity index 100% rename from Plugins/OpenAPIGeneratorCommandPlugin/plugin.swift rename to Plugins/OpenAPIGeneratorCommand/plugin.swift diff --git a/Plugins/OpenAPIGenerator/GeneratorMode.swift b/Plugins/PluginsShared/GeneratorMode.swift similarity index 100% rename from Plugins/OpenAPIGenerator/GeneratorMode.swift rename to Plugins/PluginsShared/GeneratorMode.swift From 0e009a0bf6bf6b029215d7ad74baa3d8ea1cb699 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Thu, 13 Jul 2023 13:25:42 +0330 Subject: [PATCH 036/100] command plugin by default run on all targets --- Plugins/OpenAPIGeneratorCommand/plugin.swift | 146 ++++++++++++++----- Plugins/PluginsShared/PluginError.swift | 3 + 2 files changed, 113 insertions(+), 36 deletions(-) diff --git a/Plugins/OpenAPIGeneratorCommand/plugin.swift b/Plugins/OpenAPIGeneratorCommand/plugin.swift index f3e0a36f..e5578e49 100644 --- a/Plugins/OpenAPIGeneratorCommand/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommand/plugin.swift @@ -43,26 +43,57 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { context: PluginContext, arguments: [String] ) async throws { - guard arguments.count == 2, arguments[0] == "--target" else { - throw PluginError.badArguments(arguments: arguments) + switch CommandMode(arguments: arguments) { + case .allTargets: + var hasHadASuccessfulRun = false + for target in context.package.targets { + guard let swiftTarget = target as? SwiftSourceModuleTarget else { + continue + } + do { + try runCommand( + targetWorkingDirectory: target.directory, + tool: context.tool, + sourceFiles: swiftTarget.sourceFiles, + targetName: target.name + ) + hasHadASuccessfulRun = true + } catch let error as PluginError { + // FIXME: throw the error if any config files exist but some don't exist. + switch error { + case .incompatibleTarget, .badArguments, .noTargetsFoundForCommandPlugin, .noTargetsMatchingTargetName, .tooManyTargetsMatchingTargetName, .noConfigFound, .noDocumentFound: + // We can't throw any of these errors because they only complain about + // the target not being openapi-generator compatible. + break + case .multiConfigFound, .multiDocumentFound: + // We throw these errors because it appears the target is supposed to be openapi-generator compatible, but it contains errors. + throw error + } + } catch { + print("Unknown error reported by run command for target '\(target.name)'. This is unexpected and should not happen. Please report at https://github.com/apple/swift-openapi-generator/issues") + } + } + if !hasHadASuccessfulRun { + throw PluginError.noTargetsFoundForCommandPlugin + } + case .target(let targetName): + let matchingTargets = try context.package.targets(named: [targetName]) + // `matchingTargets.count` can't be 0 because + // `targets(named:)` would throw an error for that, based on its documentation. + guard matchingTargets.count == 1 else { + throw PluginError.tooManyTargetsMatchingTargetName(targetNames: matchingTargets.map(\.name)) + } + let target = matchingTargets[0] + guard let swiftTarget = target as? SwiftSourceModuleTarget else { + throw PluginError.incompatibleTarget(targetName: target.name) + } + return try runCommand( + targetWorkingDirectory: target.directory, + tool: context.tool, + sourceFiles: swiftTarget.sourceFiles, + targetName: target.name + ) } - let targetName = arguments[1] - let matchingTargets = try context.package.targets(named: [targetName]) - // `matchingTargets.count` can't be 0 because - // `targets(named:)` would throw an error for that. - guard matchingTargets.count == 1 else { - throw PluginError.tooManyTargetsMatchingTargetName(targetNames: matchingTargets.map(\.name)) - } - let target = matchingTargets[0] - guard let swiftTarget = target as? SwiftSourceModuleTarget else { - throw PluginError.incompatibleTarget(targetName: target.name) - } - return try runCommand( - targetWorkingDirectory: target.directory, - tool: context.tool, - sourceFiles: swiftTarget.sourceFiles, - targetName: target.name - ) } } @@ -74,24 +105,67 @@ extension SwiftOpenAPIGeneratorPlugin: XcodeCommandPlugin { context: XcodePluginContext, arguments: [String] ) throws { - guard arguments.count == 2, arguments[0] == "--target" else { - throw PluginError.badArguments(arguments: arguments) + switch CommandMode(arguments: arguments) { + case .allTargets: + var hasHadASuccessfulRun = false + for xcodeTarget in context.xcodeProject.targets { + guard let target = xcodeTarget as? SourceModuleTarget else { + continue + } + do { + try runCommand( + targetWorkingDirectory: target.directory, + tool: context.tool, + sourceFiles: xcodeTarget.inputFiles, + targetName: xcodeTarget.displayName + ) + hasHadASuccessfulRun = true + } catch let error as PluginError { + switch error { + case .incompatibleTarget, .badArguments, .noTargetsFoundForCommandPlugin, .noTargetsMatchingTargetName, .tooManyTargetsMatchingTargetName, .noConfigFound, .noDocumentFound: + // We can't throw any of these errors because they only complain about + // the target not being openapi-generator compatible. + break + case .multiConfigFound, .multiDocumentFound: + // We throw these errors because it appears the target is supposed to be openapi-generator compatible, but it contains errors. + throw error + } + } catch { + print("Unknown error reported by run command for target '\(target.name)'. This is unexpected and should not happen. Please report at https://github.com/apple/swift-openapi-generator/issues") + } + } + if !hasHadASuccessfulRun { + throw PluginError.noTargetsFoundForCommandPlugin + } + case .target(let targetName): + guard let xcodeTarget = context.xcodeProject.targets.first( + where: { $0.displayName == targetName } + ) else { + throw PluginError.noTargetsMatchingTargetName(targetName: targetName) + } + guard let target = xcodeTarget as? SourceModuleTarget else { + throw PluginError.incompatibleTarget(targetName: targetName) + } + return try runCommand( + targetWorkingDirectory: target.directory, + tool: context.tool, + sourceFiles: xcodeTarget.inputFiles, + targetName: xcodeTarget.displayName + ) } - let targetName = arguments[1] - guard let xcodeTarget = context.xcodeProject.targets.first( - where: { $0.displayName == targetName } - ) else { - throw PluginError.noTargetsMatchingTargetName(targetName: targetName) - } - guard let target = xcodeTarget as? SourceModuleTarget else { - throw PluginError.incompatibleTarget(targetName: targetName) - } - return try runCommand( - targetWorkingDirectory: target.directory, - tool: context.tool, - sourceFiles: xcodeTarget.inputFiles, - targetName: xcodeTarget.displayName - ) } } #endif + +enum CommandMode { + case allTargets + case target(name: String) + + init(arguments: [String]) { + if arguments.count == 2, arguments[0] == "--target" { + self = .target(name: arguments[1]) + } else { + self = .allTargets + } + } +} diff --git a/Plugins/PluginsShared/PluginError.swift b/Plugins/PluginsShared/PluginError.swift index d2392222..110ad986 100644 --- a/Plugins/PluginsShared/PluginError.swift +++ b/Plugins/PluginsShared/PluginError.swift @@ -4,6 +4,7 @@ import Foundation enum PluginError: Swift.Error, CustomStringConvertible, LocalizedError { case incompatibleTarget(targetName: String) case badArguments(arguments: [String]) + case noTargetsFoundForCommandPlugin // The description is only suitable for Xcode, as it's only thrown in Xcode plugins. case noTargetsMatchingTargetName(targetName: String) // The description is not suitable for Xcode, as it's not thrown in Xcode plugins. @@ -20,6 +21,8 @@ enum PluginError: Swift.Error, CustomStringConvertible, LocalizedError { "Incompatible target called '\(targetName)'. Only Swift source targets can be used with the Swift OpenAPI generator plugin." case .badArguments(let arguments): return "Bad arguments provided: \(arguments). On Xcode, use Xcode's run plugin UI to choose a specific target. On CLI, pass a specific target's name to the command like so: '--target TARGET_NAME'" + case .noTargetsFoundForCommandPlugin: + return "None of the targets include valid OpenAPI spec files. Please make sure at least one of your targets has valid OpenAPI spec files before triggering this command plugin. Read the documentation to correctly set up your targets: https://swiftpackageindex.com/apple/swift-openapi-generator/documentation." case .noTargetsMatchingTargetName(let targetName): return "No target called '\(targetName)' were found. Use Xcode's UI to choose a single specific target before triggering the command plugin." case .tooManyTargetsMatchingTargetName(let targetNames): From 3b13bb4f146a434466fedce0b6ce13f48587a624 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Sat, 15 Jul 2023 09:09:09 +0330 Subject: [PATCH 037/100] Update Package.swift Co-authored-by: Honza Dvorsky --- Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index 7dbfe845..d74166d0 100644 --- a/Package.swift +++ b/Package.swift @@ -155,7 +155,7 @@ let package = Package( capability: Target.PluginCapability.command( intent: .custom( verb: "generate-openapi-code", - description: "Generates OpenAPI code" + description: "Generates Swift code from an OpenAPI document." ), permissions: [.writeToPackageDirectory(reason: "To write the generated Swift files back into the source directory of the package.")] ), From 20bdc2766483e2024ce620feb723fc551c237c1c Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Sat, 15 Jul 2023 10:59:05 +0330 Subject: [PATCH 038/100] fixes --- .../Layers/StructuredSwiftRepresentation.swift | 5 ----- .../_OpenAPIGeneratorCore/Renderer/TextBasedRenderer.swift | 3 --- .../Translator/ServerTranslator/ServerTranslator.swift | 1 - 3 files changed, 9 deletions(-) diff --git a/Sources/_OpenAPIGeneratorCore/Layers/StructuredSwiftRepresentation.swift b/Sources/_OpenAPIGeneratorCore/Layers/StructuredSwiftRepresentation.swift index 72d1ed8d..29989ae3 100644 --- a/Sources/_OpenAPIGeneratorCore/Layers/StructuredSwiftRepresentation.swift +++ b/Sources/_OpenAPIGeneratorCore/Layers/StructuredSwiftRepresentation.swift @@ -377,11 +377,6 @@ struct ParameterDescription: Equatable, Codable { /// For example, in `bar baz: String = "hi"`, `name` is `baz`. var name: String? = nil - /// The existential any keyword. - /// - /// For example, in `bar baz: any Client = ...`, `any` is the keyword referred to here. - var anyKeyword: Bool = false - /// The type name of the parameter. /// /// For example, in `bar baz: String = "hi"`, `type` is `String`. diff --git a/Sources/_OpenAPIGeneratorCore/Renderer/TextBasedRenderer.swift b/Sources/_OpenAPIGeneratorCore/Renderer/TextBasedRenderer.swift index f1049455..e0da301c 100644 --- a/Sources/_OpenAPIGeneratorCore/Renderer/TextBasedRenderer.swift +++ b/Sources/_OpenAPIGeneratorCore/Renderer/TextBasedRenderer.swift @@ -610,9 +610,6 @@ struct TextBasedRenderer: RendererProtocol { } } words.append(":") - if parameterDescription.anyKeyword { - words.append("any") - } words.append(parameterDescription.type) if let defaultValue = parameterDescription.defaultValue { words.append("=") diff --git a/Sources/_OpenAPIGeneratorCore/Translator/ServerTranslator/ServerTranslator.swift b/Sources/_OpenAPIGeneratorCore/Translator/ServerTranslator/ServerTranslator.swift index 8bd62094..32129303 100644 --- a/Sources/_OpenAPIGeneratorCore/Translator/ServerTranslator/ServerTranslator.swift +++ b/Sources/_OpenAPIGeneratorCore/Translator/ServerTranslator/ServerTranslator.swift @@ -79,7 +79,6 @@ struct ServerFileTranslator: FileTranslator { .init( label: "on", name: "transport", - anyKeyword: true, type: Constants.Server.Transport.typeName ), .init( From 91620421bd3ff773b96a8eda14a78344bc8141fc Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Sat, 15 Jul 2023 11:02:49 +0330 Subject: [PATCH 039/100] more fixes --- .../Translator/ClientTranslator/ClientTranslator.swift | 2 +- .../Translator/ServerTranslator/ServerTranslator.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/_OpenAPIGeneratorCore/Translator/ClientTranslator/ClientTranslator.swift b/Sources/_OpenAPIGeneratorCore/Translator/ClientTranslator/ClientTranslator.swift index 870691ff..c02eac5d 100644 --- a/Sources/_OpenAPIGeneratorCore/Translator/ClientTranslator/ClientTranslator.swift +++ b/Sources/_OpenAPIGeneratorCore/Translator/ClientTranslator/ClientTranslator.swift @@ -85,7 +85,7 @@ struct ClientFileTranslator: FileTranslator { ), .init( label: "middlewares", - type: "[any \(Constants.Client.Middleware.typeName)]", + type: "[\(Constants.Client.Middleware.typeName)]", defaultValue: .literal(.array([])) ), ], diff --git a/Sources/_OpenAPIGeneratorCore/Translator/ServerTranslator/ServerTranslator.swift b/Sources/_OpenAPIGeneratorCore/Translator/ServerTranslator/ServerTranslator.swift index 32129303..9a87ea3a 100644 --- a/Sources/_OpenAPIGeneratorCore/Translator/ServerTranslator/ServerTranslator.swift +++ b/Sources/_OpenAPIGeneratorCore/Translator/ServerTranslator/ServerTranslator.swift @@ -93,7 +93,7 @@ struct ServerFileTranslator: FileTranslator { ), .init( label: "middlewares", - type: "[any \(Constants.Server.Middleware.typeName)]", + type: "[\(Constants.Server.Middleware.typeName)]", defaultValue: .literal(.array([])) ), ], From 94f110c6ce6704728d294c37af185f3540044d81 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Sun, 16 Jul 2023 07:49:43 +0330 Subject: [PATCH 040/100] better error handling --- Plugins/OpenAPIGeneratorCommand/plugin.swift | 51 ++++++------ Plugins/PluginsShared/PluginError.swift | 67 ++++++++++++---- Plugins/PluginsShared/PluginUtils.swift | 81 +++++++++++++++----- 3 files changed, 142 insertions(+), 57 deletions(-) diff --git a/Plugins/OpenAPIGeneratorCommand/plugin.swift b/Plugins/OpenAPIGeneratorCommand/plugin.swift index e5578e49..d8d93bda 100644 --- a/Plugins/OpenAPIGeneratorCommand/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommand/plugin.swift @@ -58,19 +58,8 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { targetName: target.name ) hasHadASuccessfulRun = true - } catch let error as PluginError { - // FIXME: throw the error if any config files exist but some don't exist. - switch error { - case .incompatibleTarget, .badArguments, .noTargetsFoundForCommandPlugin, .noTargetsMatchingTargetName, .tooManyTargetsMatchingTargetName, .noConfigFound, .noDocumentFound: - // We can't throw any of these errors because they only complain about - // the target not being openapi-generator compatible. - break - case .multiConfigFound, .multiDocumentFound: - // We throw these errors because it appears the target is supposed to be openapi-generator compatible, but it contains errors. - throw error - } } catch { - print("Unknown error reported by run command for target '\(target.name)'. This is unexpected and should not happen. Please report at https://github.com/apple/swift-openapi-generator/issues") + try throwErrorIfNecessary(error, targetName: target.name) } } if !hasHadASuccessfulRun { @@ -120,18 +109,8 @@ extension SwiftOpenAPIGeneratorPlugin: XcodeCommandPlugin { targetName: xcodeTarget.displayName ) hasHadASuccessfulRun = true - } catch let error as PluginError { - switch error { - case .incompatibleTarget, .badArguments, .noTargetsFoundForCommandPlugin, .noTargetsMatchingTargetName, .tooManyTargetsMatchingTargetName, .noConfigFound, .noDocumentFound: - // We can't throw any of these errors because they only complain about - // the target not being openapi-generator compatible. - break - case .multiConfigFound, .multiDocumentFound: - // We throw these errors because it appears the target is supposed to be openapi-generator compatible, but it contains errors. - throw error - } } catch { - print("Unknown error reported by run command for target '\(target.name)'. This is unexpected and should not happen. Please report at https://github.com/apple/swift-openapi-generator/issues") + try throwErrorIfNecessary(error, targetName: target.name) } } if !hasHadASuccessfulRun { @@ -169,3 +148,29 @@ enum CommandMode { } } } + +extension SwiftOpenAPIGeneratorPlugin { + func throwErrorIfNecessary(_ error: any Error, targetName: String) throws { + if let error = error as? PluginError { + switch error { + case .fileErrors(let errors): + if errors.count != FileError.Kind.allCases.count { + // There are some file-finding errors but at least 1 file is available. + // This means the user means to use the target with the generator, just + // hasn't configured their target properly. + // We'll throw this error to let them know. + throw error + } else { + break + } + default: + // We can't throw any of these errors because they only complain about + // the target not being openapi-generator compatible. + break + } + } else { + print("Unknown error reported by run command for target '\(targetName)'. This is unexpected and should not happen. Please report at https://github.com/apple/swift-openapi-generator/issues") + // Don't throw the error to not interrupt the process. + } + } +} diff --git a/Plugins/PluginsShared/PluginError.swift b/Plugins/PluginsShared/PluginError.swift index 110ad986..9e9bba56 100644 --- a/Plugins/PluginsShared/PluginError.swift +++ b/Plugins/PluginsShared/PluginError.swift @@ -9,10 +9,7 @@ enum PluginError: Swift.Error, CustomStringConvertible, LocalizedError { case noTargetsMatchingTargetName(targetName: String) // The description is not suitable for Xcode, as it's not thrown in Xcode plugins. case tooManyTargetsMatchingTargetName(targetNames: [String]) - case noConfigFound(targetName: String) - case noDocumentFound(targetName: String) - case multiConfigFound(targetName: String, files: [Path]) - case multiDocumentFound(targetName: String, files: [Path]) + case fileErrors([FileError]) var description: String { switch self { @@ -27,18 +24,56 @@ enum PluginError: Swift.Error, CustomStringConvertible, LocalizedError { return "No target called '\(targetName)' were found. Use Xcode's UI to choose a single specific target before triggering the command plugin." case .tooManyTargetsMatchingTargetName(let targetNames): return "Too many targets found matching the provided target name: '\(targetNames)'. Target name must be specific enough for the plugin to only find a single target." - case .noConfigFound(let targetName): - return - "No config file found in the target named '\(targetName)'. Add a file called 'openapi-generator-config.yaml' or 'openapi-generator-config.yml' to the target's source directory. See documentation for details." - case .noDocumentFound(let targetName): - return - "No OpenAPI document found in the target named '\(targetName)'. Add a file called 'openapi.yaml', 'openapi.yml' or 'openapi.json' (can also be a symlink) to the target's source directory. See documentation for details." - case .multiConfigFound(let targetName, let files): - return - "Multiple config files found in the target named '\(targetName)', but exactly one is required. Found \(files.map(\.description).joined(separator: " "))." - case .multiDocumentFound(let targetName, let files): - return - "Multiple OpenAPI documents found in the target named '\(targetName)', but exactly one is required. Found \(files.map(\.description).joined(separator: " "))." + case .fileErrors(let errors): + return "Found file errors: \(errors.description)" + } + } + + var errorDescription: String? { + description + } +} + +struct FileError: Swift.Error, CustomStringConvertible, LocalizedError { + + enum Kind: CaseIterable { + case config + case document + } + + enum Issue { + case notFound + case multiFound(files: [Path]) + } + + let targetName: String + let fileKind: Kind + let issue: Issue + + var description: String { + "FileError { targetName: \(targetName), fileKind: \(fileKind), description: \(preciseErrorDescription) }" + } + + var preciseErrorDescription: String { + switch fileKind { + case .config: + switch issue { + case .notFound: + return + "No config file found in the target named '\(targetName)'. Add a file called 'openapi-generator-config.yaml' or 'openapi-generator-config.yml' to the target's source directory. See documentation for details." + case .multiFound(let files): + return + "Multiple config files found in the target named '\(targetName)', but exactly one is expected. Found \(files.map(\.description).joined(separator: " "))." + } + case .document: + switch issue { + case .notFound: + return + "No OpenAPI document found in the target named '\(targetName)'. Add a file called 'openapi.yaml', 'openapi.yml' or 'openapi.json' (can also be a symlink) to the target's source directory. See documentation for details." + case .multiFound(let files): + return + "Multiple OpenAPI documents found in the target named '\(targetName)', but exactly one is expected. Found \(files.map(\.description).joined(separator: " "))." + } } } diff --git a/Plugins/PluginsShared/PluginUtils.swift b/Plugins/PluginsShared/PluginUtils.swift index 5f5a1b0c..046d4edc 100644 --- a/Plugins/PluginsShared/PluginUtils.swift +++ b/Plugins/PluginsShared/PluginUtils.swift @@ -19,24 +19,7 @@ enum PluginUtils { targetName: String, invocationSource: InvocationSource ) throws -> ValidatedInputs { - let inputFiles = sourceFiles - let matchedConfigs = inputFiles.filter { supportedConfigFiles.contains($0.path.lastComponent) }.map(\.path) - guard matchedConfigs.count > 0 else { - throw PluginError.noConfigFound(targetName: targetName) - } - guard matchedConfigs.count == 1 else { - throw PluginError.multiConfigFound(targetName: targetName, files: matchedConfigs) - } - let config = matchedConfigs[0] - - let matchedDocs = inputFiles.filter { supportedDocFiles.contains($0.path.lastComponent) }.map(\.path) - guard matchedDocs.count > 0 else { - throw PluginError.noDocumentFound(targetName: targetName) - } - guard matchedDocs.count == 1 else { - throw PluginError.multiDocumentFound(targetName: targetName, files: matchedDocs) - } - let doc = matchedDocs[0] + let (config, doc) = try findFiles(inputFiles: sourceFiles, targetName: targetName) let genSourcesDir = workingDirectory.appending("GeneratedSources") let arguments = [ @@ -56,4 +39,66 @@ enum PluginUtils { tool: tool ) } + + private static func findFiles( + inputFiles: FileList, + targetName: String + ) throws -> (config: Path, doc: Path) { + let config = findConfig(inputFiles: inputFiles, targetName: targetName) + let doc = findDocument(inputFiles: inputFiles, targetName: targetName) + switch (config, doc) { + case (.failure(let error1), .failure(let error2)): + throw PluginError.fileErrors([error1, error2]) + case (_, .failure(let error)): + throw PluginError.fileErrors([error]) + case (.failure(let error), _): + throw PluginError.fileErrors([error]) + case (.success(let config), .success(let doc)): + return (config, doc) + } + } + + private static func findConfig( + inputFiles: FileList, + targetName: String + ) -> Result { + let matchedConfigs = inputFiles.filter { supportedConfigFiles.contains($0.path.lastComponent) }.map(\.path) + guard matchedConfigs.count > 0 else { + return .failure(FileError( + targetName: targetName, + fileKind: .config, + issue: .notFound + )) + } + guard matchedConfigs.count == 1 else { + return .failure(FileError( + targetName: targetName, + fileKind: .config, + issue: .multiFound(files: matchedConfigs) + )) + } + return .success(matchedConfigs[0]) + } + + private static func findDocument( + inputFiles: FileList, + targetName: String + ) -> Result { + let matchedDocs = inputFiles.filter { supportedDocFiles.contains($0.path.lastComponent) }.map(\.path) + guard matchedDocs.count > 0 else { + return .failure(FileError( + targetName: targetName, + fileKind: .document, + issue: .notFound + )) + } + guard matchedDocs.count == 1 else { + return .failure(FileError( + targetName: targetName, + fileKind: .document, + issue: .multiFound(files: matchedDocs) + )) + } + return .success(matchedDocs[0]) + } } From b9337d29f319ffba6cb4f85cacd5695a6a005590 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Sun, 16 Jul 2023 08:24:31 +0330 Subject: [PATCH 041/100] better error reporting --- Plugins/OpenAPIGeneratorCommand/plugin.swift | 54 ++++++++++++-------- Plugins/PluginsShared/PluginError.swift | 16 ++++-- Plugins/PluginsShared/PluginUtils.swift | 6 +-- 3 files changed, 49 insertions(+), 27 deletions(-) diff --git a/Plugins/OpenAPIGeneratorCommand/plugin.swift b/Plugins/OpenAPIGeneratorCommand/plugin.swift index d8d93bda..2f90d3f5 100644 --- a/Plugins/OpenAPIGeneratorCommand/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommand/plugin.swift @@ -46,6 +46,7 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { switch CommandMode(arguments: arguments) { case .allTargets: var hasHadASuccessfulRun = false + var errors = [(error: any Error, targetName: String)]() for target in context.package.targets { guard let swiftTarget = target as? SwiftSourceModuleTarget else { continue @@ -59,12 +60,13 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { ) hasHadASuccessfulRun = true } catch { - try throwErrorIfNecessary(error, targetName: target.name) + errors.append((error, target.name)) } } if !hasHadASuccessfulRun { throw PluginError.noTargetsFoundForCommandPlugin } + try throwErrorsIfNecessary(errors) case .target(let targetName): let matchingTargets = try context.package.targets(named: [targetName]) // `matchingTargets.count` can't be 0 because @@ -97,6 +99,7 @@ extension SwiftOpenAPIGeneratorPlugin: XcodeCommandPlugin { switch CommandMode(arguments: arguments) { case .allTargets: var hasHadASuccessfulRun = false + var errors = [(error: any Error, targetName: String)]() for xcodeTarget in context.xcodeProject.targets { guard let target = xcodeTarget as? SourceModuleTarget else { continue @@ -110,12 +113,13 @@ extension SwiftOpenAPIGeneratorPlugin: XcodeCommandPlugin { ) hasHadASuccessfulRun = true } catch { - try throwErrorIfNecessary(error, targetName: target.name) + errors.append((error, target.name)) } } if !hasHadASuccessfulRun { throw PluginError.noTargetsFoundForCommandPlugin } + try throwErrorsIfNecessary(errors) case .target(let targetName): guard let xcodeTarget = context.xcodeProject.targets.first( where: { $0.displayName == targetName } @@ -150,27 +154,35 @@ enum CommandMode { } extension SwiftOpenAPIGeneratorPlugin { - func throwErrorIfNecessary(_ error: any Error, targetName: String) throws { - if let error = error as? PluginError { - switch error { - case .fileErrors(let errors): - if errors.count != FileError.Kind.allCases.count { - // There are some file-finding errors but at least 1 file is available. - // This means the user means to use the target with the generator, just - // hasn't configured their target properly. - // We'll throw this error to let them know. - throw error - } else { - break + func throwErrorsIfNecessary(_ errors: [(error: any Error, targetName: String)]) throws { + let errorsToBeReported = errors.compactMap { + (error, targetName) -> PluginError? in + if let error = error as? PluginError { + switch error { + case .fileErrors(let errors, _): + if errors.count != FileError.Kind.allCases.count { + // There are some file-finding errors but at least 1 file is available. + // This means the user means to use the target with the generator, just + // hasn't configured their target properly. + // We'll throw this error to let them know. + return error + } else { + return nil + } + default: + // We can't throw any of these errors because they only complain about + // the target not being openapi-generator compatible. + return nil } - default: - // We can't throw any of these errors because they only complain about - // the target not being openapi-generator compatible. - break + } else { + print("Unknown error reported by run command for target '\(targetName)'. This is unexpected and should not happen. Please report at https://github.com/apple/swift-openapi-generator/issues") + // Don't throw the error to not interrupt the process. + return nil } - } else { - print("Unknown error reported by run command for target '\(targetName)'. This is unexpected and should not happen. Please report at https://github.com/apple/swift-openapi-generator/issues") - // Don't throw the error to not interrupt the process. + } + + if !errorsToBeReported.isEmpty { + throw errorsToBeReported } } } diff --git a/Plugins/PluginsShared/PluginError.swift b/Plugins/PluginsShared/PluginError.swift index 9e9bba56..666a1132 100644 --- a/Plugins/PluginsShared/PluginError.swift +++ b/Plugins/PluginsShared/PluginError.swift @@ -9,7 +9,7 @@ enum PluginError: Swift.Error, CustomStringConvertible, LocalizedError { case noTargetsMatchingTargetName(targetName: String) // The description is not suitable for Xcode, as it's not thrown in Xcode plugins. case tooManyTargetsMatchingTargetName(targetNames: [String]) - case fileErrors([FileError]) + case fileErrors([FileError], targetName: String) var description: String { switch self { @@ -24,8 +24,8 @@ enum PluginError: Swift.Error, CustomStringConvertible, LocalizedError { return "No target called '\(targetName)' were found. Use Xcode's UI to choose a single specific target before triggering the command plugin." case .tooManyTargetsMatchingTargetName(let targetNames): return "Too many targets found matching the provided target name: '\(targetNames)'. Target name must be specific enough for the plugin to only find a single target." - case .fileErrors(let errors): - return "Found file errors: \(errors.description)" + case .fileErrors(let errors, let targetName): + return "Found file errors in target called '\(targetName)': \(errors.description)" } } @@ -34,6 +34,16 @@ enum PluginError: Swift.Error, CustomStringConvertible, LocalizedError { } } +extension [PluginError]: Swift.Error, CustomStringConvertible, LocalizedError { + public var description: String { + "Multiple Plugin Errors { \(self.map(\.description).joined(separator: ",")) }" + } + + public var errorDescription: String? { + description + } +} + struct FileError: Swift.Error, CustomStringConvertible, LocalizedError { enum Kind: CaseIterable { diff --git a/Plugins/PluginsShared/PluginUtils.swift b/Plugins/PluginsShared/PluginUtils.swift index 046d4edc..95f32d4c 100644 --- a/Plugins/PluginsShared/PluginUtils.swift +++ b/Plugins/PluginsShared/PluginUtils.swift @@ -48,11 +48,11 @@ enum PluginUtils { let doc = findDocument(inputFiles: inputFiles, targetName: targetName) switch (config, doc) { case (.failure(let error1), .failure(let error2)): - throw PluginError.fileErrors([error1, error2]) + throw PluginError.fileErrors([error1, error2], targetName: targetName) case (_, .failure(let error)): - throw PluginError.fileErrors([error]) + throw PluginError.fileErrors([error], targetName: targetName) case (.failure(let error), _): - throw PluginError.fileErrors([error]) + throw PluginError.fileErrors([error], targetName: targetName) case (.success(let config), .success(let doc)): return (config, doc) } From b2cfb69405e9dc1dd637e11c5004600c189a27cb Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Sun, 16 Jul 2023 08:33:35 +0330 Subject: [PATCH 042/100] minor refinements --- Plugins/OpenAPIGeneratorCommand/plugin.swift | 1 + Plugins/PluginsShared/PluginError.swift | 4 ---- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/Plugins/OpenAPIGeneratorCommand/plugin.swift b/Plugins/OpenAPIGeneratorCommand/plugin.swift index 2f90d3f5..90e14f75 100644 --- a/Plugins/OpenAPIGeneratorCommand/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommand/plugin.swift @@ -172,6 +172,7 @@ extension SwiftOpenAPIGeneratorPlugin { default: // We can't throw any of these errors because they only complain about // the target not being openapi-generator compatible. + // We can't expect all targets to be OpenAPI compatible. return nil } } else { diff --git a/Plugins/PluginsShared/PluginError.swift b/Plugins/PluginsShared/PluginError.swift index 666a1132..17923d33 100644 --- a/Plugins/PluginsShared/PluginError.swift +++ b/Plugins/PluginsShared/PluginError.swift @@ -35,10 +35,6 @@ enum PluginError: Swift.Error, CustomStringConvertible, LocalizedError { } extension [PluginError]: Swift.Error, CustomStringConvertible, LocalizedError { - public var description: String { - "Multiple Plugin Errors { \(self.map(\.description).joined(separator: ",")) }" - } - public var errorDescription: String? { description } From 3dd92f0a17d6fa9548b9ec48fa798aed0f122aa7 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Sun, 16 Jul 2023 08:42:52 +0330 Subject: [PATCH 043/100] better errors --- Plugins/OpenAPIGeneratorCommand/plugin.swift | 12 +++++++++--- Plugins/PluginsShared/PluginError.swift | 11 +++++++---- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/Plugins/OpenAPIGeneratorCommand/plugin.swift b/Plugins/OpenAPIGeneratorCommand/plugin.swift index 90e14f75..bac7065d 100644 --- a/Plugins/OpenAPIGeneratorCommand/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommand/plugin.swift @@ -43,7 +43,7 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { context: PluginContext, arguments: [String] ) async throws { - switch CommandMode(arguments: arguments) { + switch try CommandMode(arguments: arguments, fromXcode: false) { case .allTargets: var hasHadASuccessfulRun = false var errors = [(error: any Error, targetName: String)]() @@ -96,7 +96,7 @@ extension SwiftOpenAPIGeneratorPlugin: XcodeCommandPlugin { context: XcodePluginContext, arguments: [String] ) throws { - switch CommandMode(arguments: arguments) { + switch try CommandMode(arguments: arguments, fromXcode: true) { case .allTargets: var hasHadASuccessfulRun = false var errors = [(error: any Error, targetName: String)]() @@ -144,9 +144,15 @@ enum CommandMode { case allTargets case target(name: String) - init(arguments: [String]) { + init(arguments: [String], fromXcode: Bool) throws { if arguments.count == 2, arguments[0] == "--target" { self = .target(name: arguments[1]) + } else if arguments.count != 0 { + if fromXcode { + throw PluginError.badArgumentsXcode(arguments: arguments) + } else { + throw PluginError.badArgumentsCLI(arguments: arguments) + } } else { self = .allTargets } diff --git a/Plugins/PluginsShared/PluginError.swift b/Plugins/PluginsShared/PluginError.swift index 17923d33..f2dc10e5 100644 --- a/Plugins/PluginsShared/PluginError.swift +++ b/Plugins/PluginsShared/PluginError.swift @@ -3,7 +3,8 @@ import Foundation enum PluginError: Swift.Error, CustomStringConvertible, LocalizedError { case incompatibleTarget(targetName: String) - case badArguments(arguments: [String]) + case badArgumentsXcode(arguments: [String]) + case badArgumentsCLI(arguments: [String]) case noTargetsFoundForCommandPlugin // The description is only suitable for Xcode, as it's only thrown in Xcode plugins. case noTargetsMatchingTargetName(targetName: String) @@ -16,10 +17,12 @@ enum PluginError: Swift.Error, CustomStringConvertible, LocalizedError { case .incompatibleTarget(let targetName): return "Incompatible target called '\(targetName)'. Only Swift source targets can be used with the Swift OpenAPI generator plugin." - case .badArguments(let arguments): - return "Bad arguments provided: \(arguments). On Xcode, use Xcode's run plugin UI to choose a specific target. On CLI, pass a specific target's name to the command like so: '--target TARGET_NAME'" + case .badArgumentsCLI(let arguments): + return "Bad arguments provided: \(arguments). Only arguments of form '--target TARGET_NAME' are supported so the generator only acts on a specific target." + case .badArgumentsXcode(let arguments): + return "Bad arguments provided: \(arguments). On Xcode, use Xcode's run plugin UI to choose a specific target." case .noTargetsFoundForCommandPlugin: - return "None of the targets include valid OpenAPI spec files. Please make sure at least one of your targets has valid OpenAPI spec files before triggering this command plugin. Read the documentation to correctly set up your targets: https://swiftpackageindex.com/apple/swift-openapi-generator/documentation." + return "None of the targets include valid OpenAPI spec files. Please make sure at least one of your targets has any valid OpenAPI spec files before triggering this command plugin. See documentation for details." case .noTargetsMatchingTargetName(let targetName): return "No target called '\(targetName)' were found. Use Xcode's UI to choose a single specific target before triggering the command plugin." case .tooManyTargetsMatchingTargetName(let targetNames): From c14a4b976a37f3eda5e1135796cb5ebd43bb872b Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Sun, 16 Jul 2023 08:44:49 +0330 Subject: [PATCH 044/100] better errors --- Plugins/OpenAPIGeneratorCommand/plugin.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Plugins/OpenAPIGeneratorCommand/plugin.swift b/Plugins/OpenAPIGeneratorCommand/plugin.swift index bac7065d..da0f930b 100644 --- a/Plugins/OpenAPIGeneratorCommand/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommand/plugin.swift @@ -63,10 +63,10 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { errors.append((error, target.name)) } } + try throwErrorsIfNecessary(errors) if !hasHadASuccessfulRun { throw PluginError.noTargetsFoundForCommandPlugin } - try throwErrorsIfNecessary(errors) case .target(let targetName): let matchingTargets = try context.package.targets(named: [targetName]) // `matchingTargets.count` can't be 0 because @@ -116,10 +116,10 @@ extension SwiftOpenAPIGeneratorPlugin: XcodeCommandPlugin { errors.append((error, target.name)) } } + try throwErrorsIfNecessary(errors) if !hasHadASuccessfulRun { throw PluginError.noTargetsFoundForCommandPlugin } - try throwErrorsIfNecessary(errors) case .target(let targetName): guard let xcodeTarget = context.xcodeProject.targets.first( where: { $0.displayName == targetName } From 0b920742f3787d1af36c22f2cf04afdb945e21ae Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Sun, 16 Jul 2023 09:12:07 +0330 Subject: [PATCH 045/100] better arguments parsing --- Plugins/OpenAPIGeneratorCommand/plugin.swift | 158 ++++++++----------- Plugins/PluginsShared/PluginError.swift | 17 +- 2 files changed, 69 insertions(+), 106 deletions(-) diff --git a/Plugins/OpenAPIGeneratorCommand/plugin.swift b/Plugins/OpenAPIGeneratorCommand/plugin.swift index da0f930b..792b16fd 100644 --- a/Plugins/OpenAPIGeneratorCommand/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommand/plugin.swift @@ -43,47 +43,38 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { context: PluginContext, arguments: [String] ) async throws { - switch try CommandMode(arguments: arguments, fromXcode: false) { - case .allTargets: - var hasHadASuccessfulRun = false - var errors = [(error: any Error, targetName: String)]() - for target in context.package.targets { - guard let swiftTarget = target as? SwiftSourceModuleTarget else { - continue - } - do { - try runCommand( - targetWorkingDirectory: target.directory, - tool: context.tool, - sourceFiles: swiftTarget.sourceFiles, - targetName: target.name - ) - hasHadASuccessfulRun = true - } catch { - errors.append((error, target.name)) - } - } - try throwErrorsIfNecessary(errors) - if !hasHadASuccessfulRun { - throw PluginError.noTargetsFoundForCommandPlugin - } - case .target(let targetName): - let matchingTargets = try context.package.targets(named: [targetName]) - // `matchingTargets.count` can't be 0 because - // `targets(named:)` would throw an error for that, based on its documentation. - guard matchingTargets.count == 1 else { - throw PluginError.tooManyTargetsMatchingTargetName(targetNames: matchingTargets.map(\.name)) - } - let target = matchingTargets[0] + var targets: [any Target] = [] + /// On CLI, we run the generator on all targets if no target names are passed + if arguments.isEmpty { + targets = context.package.targets + print("Running OpenAPI generator CommandPlugin all targets") + } else { + let targetNames = try parseTargetNames(arguments: arguments) + print("Running OpenAPI generator CommandPlugin on targets: \(targetNames)") + targets = try context.package.targets(named: Array(targetNames)) + } + + var hasHadASuccessfulRun = false + var errors = [(error: any Error, targetName: String)]() + for target in targets { guard let swiftTarget = target as? SwiftSourceModuleTarget else { - throw PluginError.incompatibleTarget(targetName: target.name) + continue + } + do { + try runCommand( + targetWorkingDirectory: target.directory, + tool: context.tool, + sourceFiles: swiftTarget.sourceFiles, + targetName: target.name + ) + hasHadASuccessfulRun = true + } catch { + errors.append((error, target.name)) } - return try runCommand( - targetWorkingDirectory: target.directory, - tool: context.tool, - sourceFiles: swiftTarget.sourceFiles, - targetName: target.name - ) + } + try throwErrorsIfNecessary(errors) + if !hasHadASuccessfulRun { + throw PluginError.noTargetsFoundForCommandPlugin } } } @@ -96,70 +87,53 @@ extension SwiftOpenAPIGeneratorPlugin: XcodeCommandPlugin { context: XcodePluginContext, arguments: [String] ) throws { - switch try CommandMode(arguments: arguments, fromXcode: true) { - case .allTargets: - var hasHadASuccessfulRun = false - var errors = [(error: any Error, targetName: String)]() - for xcodeTarget in context.xcodeProject.targets { - guard let target = xcodeTarget as? SourceModuleTarget else { - continue - } - do { - try runCommand( - targetWorkingDirectory: target.directory, - tool: context.tool, - sourceFiles: xcodeTarget.inputFiles, - targetName: xcodeTarget.displayName - ) - hasHadASuccessfulRun = true - } catch { - errors.append((error, target.name)) - } - } - try throwErrorsIfNecessary(errors) - if !hasHadASuccessfulRun { - throw PluginError.noTargetsFoundForCommandPlugin - } - case .target(let targetName): - guard let xcodeTarget = context.xcodeProject.targets.first( - where: { $0.displayName == targetName } - ) else { - throw PluginError.noTargetsMatchingTargetName(targetName: targetName) - } + // On Xcode, it automatically includes all targets when you run the plugin. + let targetNames = try parseTargetNames(arguments: arguments) + print("Running OpenAPI generator CommandPlugin on targets: \(targetNames)") + let targets = context.xcodeProject.targets.filter { targetNames.contains($0.displayName) } + var hasHadASuccessfulRun = false + var errors = [(error: any Error, targetName: String)]() + for xcodeTarget in targets { guard let target = xcodeTarget as? SourceModuleTarget else { - throw PluginError.incompatibleTarget(targetName: targetName) + continue } - return try runCommand( - targetWorkingDirectory: target.directory, - tool: context.tool, - sourceFiles: xcodeTarget.inputFiles, - targetName: xcodeTarget.displayName - ) + do { + try runCommand( + targetWorkingDirectory: target.directory, + tool: context.tool, + sourceFiles: xcodeTarget.inputFiles, + targetName: xcodeTarget.displayName + ) + hasHadASuccessfulRun = true + } catch { + errors.append((error, target.name)) + } + } + try throwErrorsIfNecessary(errors) + if !hasHadASuccessfulRun { + throw PluginError.noTargetsFoundForCommandPlugin } } } #endif -enum CommandMode { - case allTargets - case target(name: String) - - init(arguments: [String], fromXcode: Bool) throws { - if arguments.count == 2, arguments[0] == "--target" { - self = .target(name: arguments[1]) - } else if arguments.count != 0 { - if fromXcode { - throw PluginError.badArgumentsXcode(arguments: arguments) +extension SwiftOpenAPIGeneratorPlugin { + func parseTargetNames(arguments: [String]) throws -> Set { + guard arguments.count % 2 == 0 else { + throw PluginError.badArguments(arguments: arguments) + } + var targets: Set = [] + targets.reserveCapacity(arguments.count / 2) + for idx in 0.. PluginError? in diff --git a/Plugins/PluginsShared/PluginError.swift b/Plugins/PluginsShared/PluginError.swift index f2dc10e5..5c2d8772 100644 --- a/Plugins/PluginsShared/PluginError.swift +++ b/Plugins/PluginsShared/PluginError.swift @@ -3,13 +3,8 @@ import Foundation enum PluginError: Swift.Error, CustomStringConvertible, LocalizedError { case incompatibleTarget(targetName: String) - case badArgumentsXcode(arguments: [String]) - case badArgumentsCLI(arguments: [String]) + case badArguments(arguments: [String]) case noTargetsFoundForCommandPlugin - // The description is only suitable for Xcode, as it's only thrown in Xcode plugins. - case noTargetsMatchingTargetName(targetName: String) - // The description is not suitable for Xcode, as it's not thrown in Xcode plugins. - case tooManyTargetsMatchingTargetName(targetNames: [String]) case fileErrors([FileError], targetName: String) var description: String { @@ -17,16 +12,10 @@ enum PluginError: Swift.Error, CustomStringConvertible, LocalizedError { case .incompatibleTarget(let targetName): return "Incompatible target called '\(targetName)'. Only Swift source targets can be used with the Swift OpenAPI generator plugin." - case .badArgumentsCLI(let arguments): - return "Bad arguments provided: \(arguments). Only arguments of form '--target TARGET_NAME' are supported so the generator only acts on a specific target." - case .badArgumentsXcode(let arguments): - return "Bad arguments provided: \(arguments). On Xcode, use Xcode's run plugin UI to choose a specific target." + case .badArguments(let arguments): + return "Bad arguments provided: \(arguments). One or more arguments of form '--target TARGET_NAME' are supported so the generator only acts on specific targets." case .noTargetsFoundForCommandPlugin: return "None of the targets include valid OpenAPI spec files. Please make sure at least one of your targets has any valid OpenAPI spec files before triggering this command plugin. See documentation for details." - case .noTargetsMatchingTargetName(let targetName): - return "No target called '\(targetName)' were found. Use Xcode's UI to choose a single specific target before triggering the command plugin." - case .tooManyTargetsMatchingTargetName(let targetNames): - return "Too many targets found matching the provided target name: '\(targetNames)'. Target name must be specific enough for the plugin to only find a single target." case .fileErrors(let errors, let targetName): return "Found file errors in target called '\(targetName)': \(errors.description)" } From 0d410a8d55624b0c2f62b39fc4b9e6f834c7ea0c Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Sun, 16 Jul 2023 09:18:11 +0330 Subject: [PATCH 046/100] fixes --- Plugins/OpenAPIGeneratorCommand/plugin.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Plugins/OpenAPIGeneratorCommand/plugin.swift b/Plugins/OpenAPIGeneratorCommand/plugin.swift index 792b16fd..0e419fa8 100644 --- a/Plugins/OpenAPIGeneratorCommand/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommand/plugin.swift @@ -124,7 +124,8 @@ extension SwiftOpenAPIGeneratorPlugin { } var targets: Set = [] targets.reserveCapacity(arguments.count / 2) - for idx in 0.. Date: Sun, 16 Jul 2023 09:35:43 +0330 Subject: [PATCH 047/100] better errors again --- Plugins/OpenAPIGeneratorCommand/plugin.swift | 37 +++++++++++++------- Plugins/PluginsShared/PluginError.swift | 3 ++ 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/Plugins/OpenAPIGeneratorCommand/plugin.swift b/Plugins/OpenAPIGeneratorCommand/plugin.swift index 0e419fa8..a128ecd9 100644 --- a/Plugins/OpenAPIGeneratorCommand/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommand/plugin.swift @@ -52,6 +52,9 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { let targetNames = try parseTargetNames(arguments: arguments) print("Running OpenAPI generator CommandPlugin on targets: \(targetNames)") targets = try context.package.targets(named: Array(targetNames)) + guard !targets.isEmpty else { + throw PluginError.noTargetsMatchingTargetNames(targetNames: Array(targetNames)) + } } var hasHadASuccessfulRun = false @@ -72,7 +75,7 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { errors.append((error, target.name)) } } - try throwErrorsIfNecessary(errors) + try throwErrorsIfNecessary(errors, targetCount: targets.count) if !hasHadASuccessfulRun { throw PluginError.noTargetsFoundForCommandPlugin } @@ -90,26 +93,28 @@ extension SwiftOpenAPIGeneratorPlugin: XcodeCommandPlugin { // On Xcode, it automatically includes all targets when you run the plugin. let targetNames = try parseTargetNames(arguments: arguments) print("Running OpenAPI generator CommandPlugin on targets: \(targetNames)") - let targets = context.xcodeProject.targets.filter { targetNames.contains($0.displayName) } + let targets = context.xcodeProject.targets + .compactMap { $0 as? SourceModuleTarget } + .filter { targetNames.contains($0.name) } + guard !targets.isEmpty else { + throw PluginError.noTargetsMatchingTargetNames(targetNames: Array(targetNames)) + } var hasHadASuccessfulRun = false var errors = [(error: any Error, targetName: String)]() - for xcodeTarget in targets { - guard let target = xcodeTarget as? SourceModuleTarget else { - continue - } + for target in targets { do { try runCommand( targetWorkingDirectory: target.directory, tool: context.tool, - sourceFiles: xcodeTarget.inputFiles, - targetName: xcodeTarget.displayName + sourceFiles: target.sourceFiles, + targetName: target.name ) hasHadASuccessfulRun = true } catch { errors.append((error, target.name)) } } - try throwErrorsIfNecessary(errors) + try throwErrorsIfNecessary(errors, targetCount: targets.count) if !hasHadASuccessfulRun { throw PluginError.noTargetsFoundForCommandPlugin } @@ -135,13 +140,21 @@ extension SwiftOpenAPIGeneratorPlugin { return targets } - func throwErrorsIfNecessary(_ errors: [(error: any Error, targetName: String)]) throws { + func throwErrorsIfNecessary( + _ errors: [(error: any Error, targetName: String)], + targetCount: Int + ) throws { + // Always throw any existing error if only one target. + if targetCount == 1, errors.count == 1 { + throw errors[0].error + } + let errorsToBeReported = errors.compactMap { (error, targetName) -> PluginError? in if let error = error as? PluginError { switch error { - case .fileErrors(let errors, _): - if errors.count != FileError.Kind.allCases.count { + case .fileErrors(let fileErrors, _): + if fileErrors.count != FileError.Kind.allCases.count { // There are some file-finding errors but at least 1 file is available. // This means the user means to use the target with the generator, just // hasn't configured their target properly. diff --git a/Plugins/PluginsShared/PluginError.swift b/Plugins/PluginsShared/PluginError.swift index 5c2d8772..23840d33 100644 --- a/Plugins/PluginsShared/PluginError.swift +++ b/Plugins/PluginsShared/PluginError.swift @@ -5,6 +5,7 @@ enum PluginError: Swift.Error, CustomStringConvertible, LocalizedError { case incompatibleTarget(targetName: String) case badArguments(arguments: [String]) case noTargetsFoundForCommandPlugin + case noTargetsMatchingTargetNames(targetNames: [String]) case fileErrors([FileError], targetName: String) var description: String { @@ -16,6 +17,8 @@ enum PluginError: Swift.Error, CustomStringConvertible, LocalizedError { return "Bad arguments provided: \(arguments). One or more arguments of form '--target TARGET_NAME' are supported so the generator only acts on specific targets." case .noTargetsFoundForCommandPlugin: return "None of the targets include valid OpenAPI spec files. Please make sure at least one of your targets has any valid OpenAPI spec files before triggering this command plugin. See documentation for details." + case .noTargetsMatchingTargetNames(let targetNames): + return "No targets with names \(targetNames) were found. Make sure the input target names are valid." case .fileErrors(let errors, let targetName): return "Found file errors in target called '\(targetName)': \(errors.description)" } From 5d14ae1df8526a3453e0f9ff9fa273c7110cb421 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Sun, 16 Jul 2023 09:43:02 +0330 Subject: [PATCH 048/100] Update plugin.swift --- Plugins/OpenAPIGeneratorCommand/plugin.swift | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Plugins/OpenAPIGeneratorCommand/plugin.swift b/Plugins/OpenAPIGeneratorCommand/plugin.swift index a128ecd9..dd5dc55e 100644 --- a/Plugins/OpenAPIGeneratorCommand/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommand/plugin.swift @@ -90,12 +90,16 @@ extension SwiftOpenAPIGeneratorPlugin: XcodeCommandPlugin { context: XcodePluginContext, arguments: [String] ) throws { - // On Xcode, it automatically includes all targets when you run the plugin. + // On Xcode, it automatically includes all targets of the scheme when you run the plugin. + // We search for recursive dependencies to make sure we don't miss a target just because of + // scheme settings. let targetNames = try parseTargetNames(arguments: arguments) - print("Running OpenAPI generator CommandPlugin on targets: \(targetNames)") let targets = context.xcodeProject.targets .compactMap { $0 as? SourceModuleTarget } .filter { targetNames.contains($0.name) } + .flatMap { $0.recursiveTargetDependencies } + .compactMap { $0 as? SourceModuleTarget } + print("Running OpenAPI generator CommandPlugin on targets: \(targets.map(\.name))") guard !targets.isEmpty else { throw PluginError.noTargetsMatchingTargetNames(targetNames: Array(targetNames)) } From ccdf3c990204cbb627dce399876801c0d19fe4c2 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Sun, 16 Jul 2023 09:48:14 +0330 Subject: [PATCH 049/100] Update plugin.swift --- Plugins/OpenAPIGeneratorCommand/plugin.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Plugins/OpenAPIGeneratorCommand/plugin.swift b/Plugins/OpenAPIGeneratorCommand/plugin.swift index dd5dc55e..d6ec4033 100644 --- a/Plugins/OpenAPIGeneratorCommand/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommand/plugin.swift @@ -50,7 +50,7 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { print("Running OpenAPI generator CommandPlugin all targets") } else { let targetNames = try parseTargetNames(arguments: arguments) - print("Running OpenAPI generator CommandPlugin on targets: \(targetNames)") + print("Running OpenAPI generator CommandPlugin on select targets: \(targetNames)") targets = try context.package.targets(named: Array(targetNames)) guard !targets.isEmpty else { throw PluginError.noTargetsMatchingTargetNames(targetNames: Array(targetNames)) @@ -97,7 +97,7 @@ extension SwiftOpenAPIGeneratorPlugin: XcodeCommandPlugin { let targets = context.xcodeProject.targets .compactMap { $0 as? SourceModuleTarget } .filter { targetNames.contains($0.name) } - .flatMap { $0.recursiveTargetDependencies } + .flatMap { $0.dependencies } .compactMap { $0 as? SourceModuleTarget } print("Running OpenAPI generator CommandPlugin on targets: \(targets.map(\.name))") guard !targets.isEmpty else { @@ -128,6 +128,7 @@ extension SwiftOpenAPIGeneratorPlugin: XcodeCommandPlugin { extension SwiftOpenAPIGeneratorPlugin { func parseTargetNames(arguments: [String]) throws -> Set { + guard arguments.count % 2 == 0 else { throw PluginError.badArguments(arguments: arguments) } From e4b38cb45c772f512f8b1166d91eb1e3315af863 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Sun, 16 Jul 2023 09:54:20 +0330 Subject: [PATCH 050/100] Update plugin.swift --- Plugins/OpenAPIGeneratorCommand/plugin.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Plugins/OpenAPIGeneratorCommand/plugin.swift b/Plugins/OpenAPIGeneratorCommand/plugin.swift index d6ec4033..8691cdeb 100644 --- a/Plugins/OpenAPIGeneratorCommand/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommand/plugin.swift @@ -51,7 +51,9 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { } else { let targetNames = try parseTargetNames(arguments: arguments) print("Running OpenAPI generator CommandPlugin on select targets: \(targetNames)") - targets = try context.package.targets(named: Array(targetNames)) + targets = try context.package.targets(named: Array(targetNames)).flatMap { + [$0] + $0.dependencies.compactMap { $0 as? Target } + } guard !targets.isEmpty else { throw PluginError.noTargetsMatchingTargetNames(targetNames: Array(targetNames)) } @@ -97,7 +99,7 @@ extension SwiftOpenAPIGeneratorPlugin: XcodeCommandPlugin { let targets = context.xcodeProject.targets .compactMap { $0 as? SourceModuleTarget } .filter { targetNames.contains($0.name) } - .flatMap { $0.dependencies } + .flatMap { [$0] + $0.dependencies.compactMap { $0 as? Target } } .compactMap { $0 as? SourceModuleTarget } print("Running OpenAPI generator CommandPlugin on targets: \(targets.map(\.name))") guard !targets.isEmpty else { From 914d5cfe09eed418bce04e42611e9919e28b5745 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Sun, 16 Jul 2023 09:56:30 +0330 Subject: [PATCH 051/100] Revert "Update plugin.swift" This reverts commit e4b38cb45c772f512f8b1166d91eb1e3315af863. --- Plugins/OpenAPIGeneratorCommand/plugin.swift | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Plugins/OpenAPIGeneratorCommand/plugin.swift b/Plugins/OpenAPIGeneratorCommand/plugin.swift index 8691cdeb..d6ec4033 100644 --- a/Plugins/OpenAPIGeneratorCommand/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommand/plugin.swift @@ -51,9 +51,7 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { } else { let targetNames = try parseTargetNames(arguments: arguments) print("Running OpenAPI generator CommandPlugin on select targets: \(targetNames)") - targets = try context.package.targets(named: Array(targetNames)).flatMap { - [$0] + $0.dependencies.compactMap { $0 as? Target } - } + targets = try context.package.targets(named: Array(targetNames)) guard !targets.isEmpty else { throw PluginError.noTargetsMatchingTargetNames(targetNames: Array(targetNames)) } @@ -99,7 +97,7 @@ extension SwiftOpenAPIGeneratorPlugin: XcodeCommandPlugin { let targets = context.xcodeProject.targets .compactMap { $0 as? SourceModuleTarget } .filter { targetNames.contains($0.name) } - .flatMap { [$0] + $0.dependencies.compactMap { $0 as? Target } } + .flatMap { $0.dependencies } .compactMap { $0 as? SourceModuleTarget } print("Running OpenAPI generator CommandPlugin on targets: \(targets.map(\.name))") guard !targets.isEmpty else { From 02998c19c37582f60b072bb86a69ce72d2c87e94 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Sun, 16 Jul 2023 09:56:34 +0330 Subject: [PATCH 052/100] Revert "Update plugin.swift" This reverts commit ccdf3c990204cbb627dce399876801c0d19fe4c2. --- Plugins/OpenAPIGeneratorCommand/plugin.swift | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Plugins/OpenAPIGeneratorCommand/plugin.swift b/Plugins/OpenAPIGeneratorCommand/plugin.swift index d6ec4033..dd5dc55e 100644 --- a/Plugins/OpenAPIGeneratorCommand/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommand/plugin.swift @@ -50,7 +50,7 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { print("Running OpenAPI generator CommandPlugin all targets") } else { let targetNames = try parseTargetNames(arguments: arguments) - print("Running OpenAPI generator CommandPlugin on select targets: \(targetNames)") + print("Running OpenAPI generator CommandPlugin on targets: \(targetNames)") targets = try context.package.targets(named: Array(targetNames)) guard !targets.isEmpty else { throw PluginError.noTargetsMatchingTargetNames(targetNames: Array(targetNames)) @@ -97,7 +97,7 @@ extension SwiftOpenAPIGeneratorPlugin: XcodeCommandPlugin { let targets = context.xcodeProject.targets .compactMap { $0 as? SourceModuleTarget } .filter { targetNames.contains($0.name) } - .flatMap { $0.dependencies } + .flatMap { $0.recursiveTargetDependencies } .compactMap { $0 as? SourceModuleTarget } print("Running OpenAPI generator CommandPlugin on targets: \(targets.map(\.name))") guard !targets.isEmpty else { @@ -128,7 +128,6 @@ extension SwiftOpenAPIGeneratorPlugin: XcodeCommandPlugin { extension SwiftOpenAPIGeneratorPlugin { func parseTargetNames(arguments: [String]) throws -> Set { - guard arguments.count % 2 == 0 else { throw PluginError.badArguments(arguments: arguments) } From 8faa974e383eb66a717cf40844170164fb75eee0 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Sun, 16 Jul 2023 09:56:36 +0330 Subject: [PATCH 053/100] Revert "Update plugin.swift" This reverts commit 5d14ae1df8526a3453e0f9ff9fa273c7110cb421. --- Plugins/OpenAPIGeneratorCommand/plugin.swift | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Plugins/OpenAPIGeneratorCommand/plugin.swift b/Plugins/OpenAPIGeneratorCommand/plugin.swift index dd5dc55e..a128ecd9 100644 --- a/Plugins/OpenAPIGeneratorCommand/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommand/plugin.swift @@ -90,16 +90,12 @@ extension SwiftOpenAPIGeneratorPlugin: XcodeCommandPlugin { context: XcodePluginContext, arguments: [String] ) throws { - // On Xcode, it automatically includes all targets of the scheme when you run the plugin. - // We search for recursive dependencies to make sure we don't miss a target just because of - // scheme settings. + // On Xcode, it automatically includes all targets when you run the plugin. let targetNames = try parseTargetNames(arguments: arguments) + print("Running OpenAPI generator CommandPlugin on targets: \(targetNames)") let targets = context.xcodeProject.targets .compactMap { $0 as? SourceModuleTarget } .filter { targetNames.contains($0.name) } - .flatMap { $0.recursiveTargetDependencies } - .compactMap { $0 as? SourceModuleTarget } - print("Running OpenAPI generator CommandPlugin on targets: \(targets.map(\.name))") guard !targets.isEmpty else { throw PluginError.noTargetsMatchingTargetNames(targetNames: Array(targetNames)) } From 5f55d7f7c9cf5c14241168ab5c93f30f0687febe Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Sun, 16 Jul 2023 09:56:38 +0330 Subject: [PATCH 054/100] Revert "better errors again" This reverts commit a92a4a0739d1beb53b807f10f362f16d00f4b43e. --- Plugins/OpenAPIGeneratorCommand/plugin.swift | 37 +++++++------------- Plugins/PluginsShared/PluginError.swift | 3 -- 2 files changed, 12 insertions(+), 28 deletions(-) diff --git a/Plugins/OpenAPIGeneratorCommand/plugin.swift b/Plugins/OpenAPIGeneratorCommand/plugin.swift index a128ecd9..0e419fa8 100644 --- a/Plugins/OpenAPIGeneratorCommand/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommand/plugin.swift @@ -52,9 +52,6 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { let targetNames = try parseTargetNames(arguments: arguments) print("Running OpenAPI generator CommandPlugin on targets: \(targetNames)") targets = try context.package.targets(named: Array(targetNames)) - guard !targets.isEmpty else { - throw PluginError.noTargetsMatchingTargetNames(targetNames: Array(targetNames)) - } } var hasHadASuccessfulRun = false @@ -75,7 +72,7 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { errors.append((error, target.name)) } } - try throwErrorsIfNecessary(errors, targetCount: targets.count) + try throwErrorsIfNecessary(errors) if !hasHadASuccessfulRun { throw PluginError.noTargetsFoundForCommandPlugin } @@ -93,28 +90,26 @@ extension SwiftOpenAPIGeneratorPlugin: XcodeCommandPlugin { // On Xcode, it automatically includes all targets when you run the plugin. let targetNames = try parseTargetNames(arguments: arguments) print("Running OpenAPI generator CommandPlugin on targets: \(targetNames)") - let targets = context.xcodeProject.targets - .compactMap { $0 as? SourceModuleTarget } - .filter { targetNames.contains($0.name) } - guard !targets.isEmpty else { - throw PluginError.noTargetsMatchingTargetNames(targetNames: Array(targetNames)) - } + let targets = context.xcodeProject.targets.filter { targetNames.contains($0.displayName) } var hasHadASuccessfulRun = false var errors = [(error: any Error, targetName: String)]() - for target in targets { + for xcodeTarget in targets { + guard let target = xcodeTarget as? SourceModuleTarget else { + continue + } do { try runCommand( targetWorkingDirectory: target.directory, tool: context.tool, - sourceFiles: target.sourceFiles, - targetName: target.name + sourceFiles: xcodeTarget.inputFiles, + targetName: xcodeTarget.displayName ) hasHadASuccessfulRun = true } catch { errors.append((error, target.name)) } } - try throwErrorsIfNecessary(errors, targetCount: targets.count) + try throwErrorsIfNecessary(errors) if !hasHadASuccessfulRun { throw PluginError.noTargetsFoundForCommandPlugin } @@ -140,21 +135,13 @@ extension SwiftOpenAPIGeneratorPlugin { return targets } - func throwErrorsIfNecessary( - _ errors: [(error: any Error, targetName: String)], - targetCount: Int - ) throws { - // Always throw any existing error if only one target. - if targetCount == 1, errors.count == 1 { - throw errors[0].error - } - + func throwErrorsIfNecessary(_ errors: [(error: any Error, targetName: String)]) throws { let errorsToBeReported = errors.compactMap { (error, targetName) -> PluginError? in if let error = error as? PluginError { switch error { - case .fileErrors(let fileErrors, _): - if fileErrors.count != FileError.Kind.allCases.count { + case .fileErrors(let errors, _): + if errors.count != FileError.Kind.allCases.count { // There are some file-finding errors but at least 1 file is available. // This means the user means to use the target with the generator, just // hasn't configured their target properly. diff --git a/Plugins/PluginsShared/PluginError.swift b/Plugins/PluginsShared/PluginError.swift index 23840d33..5c2d8772 100644 --- a/Plugins/PluginsShared/PluginError.swift +++ b/Plugins/PluginsShared/PluginError.swift @@ -5,7 +5,6 @@ enum PluginError: Swift.Error, CustomStringConvertible, LocalizedError { case incompatibleTarget(targetName: String) case badArguments(arguments: [String]) case noTargetsFoundForCommandPlugin - case noTargetsMatchingTargetNames(targetNames: [String]) case fileErrors([FileError], targetName: String) var description: String { @@ -17,8 +16,6 @@ enum PluginError: Swift.Error, CustomStringConvertible, LocalizedError { return "Bad arguments provided: \(arguments). One or more arguments of form '--target TARGET_NAME' are supported so the generator only acts on specific targets." case .noTargetsFoundForCommandPlugin: return "None of the targets include valid OpenAPI spec files. Please make sure at least one of your targets has any valid OpenAPI spec files before triggering this command plugin. See documentation for details." - case .noTargetsMatchingTargetNames(let targetNames): - return "No targets with names \(targetNames) were found. Make sure the input target names are valid." case .fileErrors(let errors, let targetName): return "Found file errors in target called '\(targetName)': \(errors.description)" } From a27efce1108c5bc5e55fc6f7aff4dcb34cbf1979 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Sun, 16 Jul 2023 09:56:40 +0330 Subject: [PATCH 055/100] Revert "fixes" This reverts commit 0d410a8d55624b0c2f62b39fc4b9e6f834c7ea0c. --- Plugins/OpenAPIGeneratorCommand/plugin.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Plugins/OpenAPIGeneratorCommand/plugin.swift b/Plugins/OpenAPIGeneratorCommand/plugin.swift index 0e419fa8..792b16fd 100644 --- a/Plugins/OpenAPIGeneratorCommand/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommand/plugin.swift @@ -124,8 +124,7 @@ extension SwiftOpenAPIGeneratorPlugin { } var targets: Set = [] targets.reserveCapacity(arguments.count / 2) - for num in 0.. Date: Sun, 16 Jul 2023 09:56:42 +0330 Subject: [PATCH 056/100] Revert "better arguments parsing" This reverts commit 0b920742f3787d1af36c22f2cf04afdb945e21ae. --- Plugins/OpenAPIGeneratorCommand/plugin.swift | 158 +++++++++++-------- Plugins/PluginsShared/PluginError.swift | 17 +- 2 files changed, 106 insertions(+), 69 deletions(-) diff --git a/Plugins/OpenAPIGeneratorCommand/plugin.swift b/Plugins/OpenAPIGeneratorCommand/plugin.swift index 792b16fd..da0f930b 100644 --- a/Plugins/OpenAPIGeneratorCommand/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommand/plugin.swift @@ -43,38 +43,47 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { context: PluginContext, arguments: [String] ) async throws { - var targets: [any Target] = [] - /// On CLI, we run the generator on all targets if no target names are passed - if arguments.isEmpty { - targets = context.package.targets - print("Running OpenAPI generator CommandPlugin all targets") - } else { - let targetNames = try parseTargetNames(arguments: arguments) - print("Running OpenAPI generator CommandPlugin on targets: \(targetNames)") - targets = try context.package.targets(named: Array(targetNames)) - } - - var hasHadASuccessfulRun = false - var errors = [(error: any Error, targetName: String)]() - for target in targets { - guard let swiftTarget = target as? SwiftSourceModuleTarget else { - continue + switch try CommandMode(arguments: arguments, fromXcode: false) { + case .allTargets: + var hasHadASuccessfulRun = false + var errors = [(error: any Error, targetName: String)]() + for target in context.package.targets { + guard let swiftTarget = target as? SwiftSourceModuleTarget else { + continue + } + do { + try runCommand( + targetWorkingDirectory: target.directory, + tool: context.tool, + sourceFiles: swiftTarget.sourceFiles, + targetName: target.name + ) + hasHadASuccessfulRun = true + } catch { + errors.append((error, target.name)) + } } - do { - try runCommand( - targetWorkingDirectory: target.directory, - tool: context.tool, - sourceFiles: swiftTarget.sourceFiles, - targetName: target.name - ) - hasHadASuccessfulRun = true - } catch { - errors.append((error, target.name)) + try throwErrorsIfNecessary(errors) + if !hasHadASuccessfulRun { + throw PluginError.noTargetsFoundForCommandPlugin } - } - try throwErrorsIfNecessary(errors) - if !hasHadASuccessfulRun { - throw PluginError.noTargetsFoundForCommandPlugin + case .target(let targetName): + let matchingTargets = try context.package.targets(named: [targetName]) + // `matchingTargets.count` can't be 0 because + // `targets(named:)` would throw an error for that, based on its documentation. + guard matchingTargets.count == 1 else { + throw PluginError.tooManyTargetsMatchingTargetName(targetNames: matchingTargets.map(\.name)) + } + let target = matchingTargets[0] + guard let swiftTarget = target as? SwiftSourceModuleTarget else { + throw PluginError.incompatibleTarget(targetName: target.name) + } + return try runCommand( + targetWorkingDirectory: target.directory, + tool: context.tool, + sourceFiles: swiftTarget.sourceFiles, + targetName: target.name + ) } } } @@ -87,53 +96,70 @@ extension SwiftOpenAPIGeneratorPlugin: XcodeCommandPlugin { context: XcodePluginContext, arguments: [String] ) throws { - // On Xcode, it automatically includes all targets when you run the plugin. - let targetNames = try parseTargetNames(arguments: arguments) - print("Running OpenAPI generator CommandPlugin on targets: \(targetNames)") - let targets = context.xcodeProject.targets.filter { targetNames.contains($0.displayName) } - var hasHadASuccessfulRun = false - var errors = [(error: any Error, targetName: String)]() - for xcodeTarget in targets { - guard let target = xcodeTarget as? SourceModuleTarget else { - continue + switch try CommandMode(arguments: arguments, fromXcode: true) { + case .allTargets: + var hasHadASuccessfulRun = false + var errors = [(error: any Error, targetName: String)]() + for xcodeTarget in context.xcodeProject.targets { + guard let target = xcodeTarget as? SourceModuleTarget else { + continue + } + do { + try runCommand( + targetWorkingDirectory: target.directory, + tool: context.tool, + sourceFiles: xcodeTarget.inputFiles, + targetName: xcodeTarget.displayName + ) + hasHadASuccessfulRun = true + } catch { + errors.append((error, target.name)) + } } - do { - try runCommand( - targetWorkingDirectory: target.directory, - tool: context.tool, - sourceFiles: xcodeTarget.inputFiles, - targetName: xcodeTarget.displayName - ) - hasHadASuccessfulRun = true - } catch { - errors.append((error, target.name)) + try throwErrorsIfNecessary(errors) + if !hasHadASuccessfulRun { + throw PluginError.noTargetsFoundForCommandPlugin } - } - try throwErrorsIfNecessary(errors) - if !hasHadASuccessfulRun { - throw PluginError.noTargetsFoundForCommandPlugin + case .target(let targetName): + guard let xcodeTarget = context.xcodeProject.targets.first( + where: { $0.displayName == targetName } + ) else { + throw PluginError.noTargetsMatchingTargetName(targetName: targetName) + } + guard let target = xcodeTarget as? SourceModuleTarget else { + throw PluginError.incompatibleTarget(targetName: targetName) + } + return try runCommand( + targetWorkingDirectory: target.directory, + tool: context.tool, + sourceFiles: xcodeTarget.inputFiles, + targetName: xcodeTarget.displayName + ) } } } #endif -extension SwiftOpenAPIGeneratorPlugin { - func parseTargetNames(arguments: [String]) throws -> Set { - guard arguments.count % 2 == 0 else { - throw PluginError.badArguments(arguments: arguments) - } - var targets: Set = [] - targets.reserveCapacity(arguments.count / 2) - for idx in 0.. PluginError? in diff --git a/Plugins/PluginsShared/PluginError.swift b/Plugins/PluginsShared/PluginError.swift index 5c2d8772..f2dc10e5 100644 --- a/Plugins/PluginsShared/PluginError.swift +++ b/Plugins/PluginsShared/PluginError.swift @@ -3,8 +3,13 @@ import Foundation enum PluginError: Swift.Error, CustomStringConvertible, LocalizedError { case incompatibleTarget(targetName: String) - case badArguments(arguments: [String]) + case badArgumentsXcode(arguments: [String]) + case badArgumentsCLI(arguments: [String]) case noTargetsFoundForCommandPlugin + // The description is only suitable for Xcode, as it's only thrown in Xcode plugins. + case noTargetsMatchingTargetName(targetName: String) + // The description is not suitable for Xcode, as it's not thrown in Xcode plugins. + case tooManyTargetsMatchingTargetName(targetNames: [String]) case fileErrors([FileError], targetName: String) var description: String { @@ -12,10 +17,16 @@ enum PluginError: Swift.Error, CustomStringConvertible, LocalizedError { case .incompatibleTarget(let targetName): return "Incompatible target called '\(targetName)'. Only Swift source targets can be used with the Swift OpenAPI generator plugin." - case .badArguments(let arguments): - return "Bad arguments provided: \(arguments). One or more arguments of form '--target TARGET_NAME' are supported so the generator only acts on specific targets." + case .badArgumentsCLI(let arguments): + return "Bad arguments provided: \(arguments). Only arguments of form '--target TARGET_NAME' are supported so the generator only acts on a specific target." + case .badArgumentsXcode(let arguments): + return "Bad arguments provided: \(arguments). On Xcode, use Xcode's run plugin UI to choose a specific target." case .noTargetsFoundForCommandPlugin: return "None of the targets include valid OpenAPI spec files. Please make sure at least one of your targets has any valid OpenAPI spec files before triggering this command plugin. See documentation for details." + case .noTargetsMatchingTargetName(let targetName): + return "No target called '\(targetName)' were found. Use Xcode's UI to choose a single specific target before triggering the command plugin." + case .tooManyTargetsMatchingTargetName(let targetNames): + return "Too many targets found matching the provided target name: '\(targetNames)'. Target name must be specific enough for the plugin to only find a single target." case .fileErrors(let errors, let targetName): return "Found file errors in target called '\(targetName)': \(errors.description)" } From 17ee64c67a8b5c8e9a611de30e789093c0589986 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Sun, 16 Jul 2023 10:05:08 +0330 Subject: [PATCH 057/100] always run on all targets --- Plugins/OpenAPIGeneratorCommand/plugin.swift | 133 ++++++------------- Plugins/PluginsShared/PluginError.swift | 14 -- 2 files changed, 38 insertions(+), 109 deletions(-) diff --git a/Plugins/OpenAPIGeneratorCommand/plugin.swift b/Plugins/OpenAPIGeneratorCommand/plugin.swift index da0f930b..6ed45955 100644 --- a/Plugins/OpenAPIGeneratorCommand/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommand/plugin.swift @@ -43,47 +43,27 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { context: PluginContext, arguments: [String] ) async throws { - switch try CommandMode(arguments: arguments, fromXcode: false) { - case .allTargets: - var hasHadASuccessfulRun = false - var errors = [(error: any Error, targetName: String)]() - for target in context.package.targets { - guard let swiftTarget = target as? SwiftSourceModuleTarget else { - continue - } - do { - try runCommand( - targetWorkingDirectory: target.directory, - tool: context.tool, - sourceFiles: swiftTarget.sourceFiles, - targetName: target.name - ) - hasHadASuccessfulRun = true - } catch { - errors.append((error, target.name)) - } - } - try throwErrorsIfNecessary(errors) - if !hasHadASuccessfulRun { - throw PluginError.noTargetsFoundForCommandPlugin - } - case .target(let targetName): - let matchingTargets = try context.package.targets(named: [targetName]) - // `matchingTargets.count` can't be 0 because - // `targets(named:)` would throw an error for that, based on its documentation. - guard matchingTargets.count == 1 else { - throw PluginError.tooManyTargetsMatchingTargetName(targetNames: matchingTargets.map(\.name)) - } - let target = matchingTargets[0] + var hasHadASuccessfulRun = false + var errors = [(error: any Error, targetName: String)]() + for target in context.package.targets { guard let swiftTarget = target as? SwiftSourceModuleTarget else { - throw PluginError.incompatibleTarget(targetName: target.name) + continue + } + do { + try runCommand( + targetWorkingDirectory: target.directory, + tool: context.tool, + sourceFiles: swiftTarget.sourceFiles, + targetName: target.name + ) + hasHadASuccessfulRun = true + } catch { + errors.append((error, target.name)) } - return try runCommand( - targetWorkingDirectory: target.directory, - tool: context.tool, - sourceFiles: swiftTarget.sourceFiles, - targetName: target.name - ) + } + try throwErrorsIfNecessary(errors) + if !hasHadASuccessfulRun { + throw PluginError.noTargetsFoundForCommandPlugin } } } @@ -96,68 +76,31 @@ extension SwiftOpenAPIGeneratorPlugin: XcodeCommandPlugin { context: XcodePluginContext, arguments: [String] ) throws { - switch try CommandMode(arguments: arguments, fromXcode: true) { - case .allTargets: - var hasHadASuccessfulRun = false - var errors = [(error: any Error, targetName: String)]() - for xcodeTarget in context.xcodeProject.targets { - guard let target = xcodeTarget as? SourceModuleTarget else { - continue - } - do { - try runCommand( - targetWorkingDirectory: target.directory, - tool: context.tool, - sourceFiles: xcodeTarget.inputFiles, - targetName: xcodeTarget.displayName - ) - hasHadASuccessfulRun = true - } catch { - errors.append((error, target.name)) - } - } - try throwErrorsIfNecessary(errors) - if !hasHadASuccessfulRun { - throw PluginError.noTargetsFoundForCommandPlugin - } - case .target(let targetName): - guard let xcodeTarget = context.xcodeProject.targets.first( - where: { $0.displayName == targetName } - ) else { - throw PluginError.noTargetsMatchingTargetName(targetName: targetName) - } + var hasHadASuccessfulRun = false + var errors = [(error: any Error, targetName: String)]() + for xcodeTarget in context.xcodeProject.targets { guard let target = xcodeTarget as? SourceModuleTarget else { - throw PluginError.incompatibleTarget(targetName: targetName) + continue } - return try runCommand( - targetWorkingDirectory: target.directory, - tool: context.tool, - sourceFiles: xcodeTarget.inputFiles, - targetName: xcodeTarget.displayName - ) - } - } -} -#endif - -enum CommandMode { - case allTargets - case target(name: String) - - init(arguments: [String], fromXcode: Bool) throws { - if arguments.count == 2, arguments[0] == "--target" { - self = .target(name: arguments[1]) - } else if arguments.count != 0 { - if fromXcode { - throw PluginError.badArgumentsXcode(arguments: arguments) - } else { - throw PluginError.badArgumentsCLI(arguments: arguments) + do { + try runCommand( + targetWorkingDirectory: target.directory, + tool: context.tool, + sourceFiles: xcodeTarget.inputFiles, + targetName: xcodeTarget.displayName + ) + hasHadASuccessfulRun = true + } catch { + errors.append((error, target.name)) } - } else { - self = .allTargets + } + try throwErrorsIfNecessary(errors) + if !hasHadASuccessfulRun { + throw PluginError.noTargetsFoundForCommandPlugin } } } +#endif extension SwiftOpenAPIGeneratorPlugin { func throwErrorsIfNecessary(_ errors: [(error: any Error, targetName: String)]) throws { diff --git a/Plugins/PluginsShared/PluginError.swift b/Plugins/PluginsShared/PluginError.swift index f2dc10e5..ec6156ef 100644 --- a/Plugins/PluginsShared/PluginError.swift +++ b/Plugins/PluginsShared/PluginError.swift @@ -3,13 +3,7 @@ import Foundation enum PluginError: Swift.Error, CustomStringConvertible, LocalizedError { case incompatibleTarget(targetName: String) - case badArgumentsXcode(arguments: [String]) - case badArgumentsCLI(arguments: [String]) case noTargetsFoundForCommandPlugin - // The description is only suitable for Xcode, as it's only thrown in Xcode plugins. - case noTargetsMatchingTargetName(targetName: String) - // The description is not suitable for Xcode, as it's not thrown in Xcode plugins. - case tooManyTargetsMatchingTargetName(targetNames: [String]) case fileErrors([FileError], targetName: String) var description: String { @@ -17,16 +11,8 @@ enum PluginError: Swift.Error, CustomStringConvertible, LocalizedError { case .incompatibleTarget(let targetName): return "Incompatible target called '\(targetName)'. Only Swift source targets can be used with the Swift OpenAPI generator plugin." - case .badArgumentsCLI(let arguments): - return "Bad arguments provided: \(arguments). Only arguments of form '--target TARGET_NAME' are supported so the generator only acts on a specific target." - case .badArgumentsXcode(let arguments): - return "Bad arguments provided: \(arguments). On Xcode, use Xcode's run plugin UI to choose a specific target." case .noTargetsFoundForCommandPlugin: return "None of the targets include valid OpenAPI spec files. Please make sure at least one of your targets has any valid OpenAPI spec files before triggering this command plugin. See documentation for details." - case .noTargetsMatchingTargetName(let targetName): - return "No target called '\(targetName)' were found. Use Xcode's UI to choose a single specific target before triggering the command plugin." - case .tooManyTargetsMatchingTargetName(let targetNames): - return "Too many targets found matching the provided target name: '\(targetNames)'. Target name must be specific enough for the plugin to only find a single target." case .fileErrors(let errors, let targetName): return "Found file errors in target called '\(targetName)': \(errors.description)" } From 39c06229c187db29ad2ca466c90fe57908aba092 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Sun, 16 Jul 2023 12:57:32 +0330 Subject: [PATCH 058/100] soundness --- Plugins/OpenAPIGeneratorCommand/plugin.swift | 48 ++++++++++---------- Plugins/PluginsShared/PluginError.swift | 15 ++---- 2 files changed, 29 insertions(+), 34 deletions(-) diff --git a/Plugins/OpenAPIGeneratorCommand/plugin.swift b/Plugins/OpenAPIGeneratorCommand/plugin.swift index 6ed45955..d73ac4bb 100644 --- a/Plugins/OpenAPIGeneratorCommand/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommand/plugin.swift @@ -62,7 +62,7 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { } } try throwErrorsIfNecessary(errors) - if !hasHadASuccessfulRun { + guard hasHadASuccessfulRun else { throw PluginError.noTargetsFoundForCommandPlugin } } @@ -95,7 +95,7 @@ extension SwiftOpenAPIGeneratorPlugin: XcodeCommandPlugin { } } try throwErrorsIfNecessary(errors) - if !hasHadASuccessfulRun { + guard hasHadASuccessfulRun else { throw PluginError.noTargetsFoundForCommandPlugin } } @@ -103,35 +103,35 @@ extension SwiftOpenAPIGeneratorPlugin: XcodeCommandPlugin { #endif extension SwiftOpenAPIGeneratorPlugin { + /// Throws if there are any errors that show a target is definitely trying to + /// have OpenAPI generator compatibility, but is failing to. func throwErrorsIfNecessary(_ errors: [(error: any Error, targetName: String)]) throws { - let errorsToBeReported = errors.compactMap { - (error, targetName) -> PluginError? in - if let error = error as? PluginError { - switch error { - case .fileErrors(let errors, _): - if errors.count != FileError.Kind.allCases.count { - // There are some file-finding errors but at least 1 file is available. - // This means the user means to use the target with the generator, just - // hasn't configured their target properly. - // We'll throw this error to let them know. - return error - } else { - return nil - } - default: - // We can't throw any of these errors because they only complain about - // the target not being openapi-generator compatible. - // We can't expect all targets to be OpenAPI compatible. - return nil - } - } else { + let errorsToBeReported = errors.compactMap { (error, targetName) -> PluginError? in + guard let error = error as? PluginError else { print("Unknown error reported by run command for target '\(targetName)'. This is unexpected and should not happen. Please report at https://github.com/apple/swift-openapi-generator/issues") // Don't throw the error to not interrupt the process. return nil } + switch error { + case .fileErrors(let errors, _): + if errors.count != FileError.Kind.allCases.count { + // There are some file-finding errors but at least 1 file is available. + // This means the user means to use the target with the generator, just + // hasn't configured their target properly. + // We'll throw this error to let them know. + return error + } else { + return nil + } + case .incompatibleTarget, .noTargetsFoundForCommandPlugin: + // We can't throw any of these errors because they only complain about + // the target not being openapi-generator compatible. + // We can't expect all targets to be OpenAPI compatible. + return nil + } } - if !errorsToBeReported.isEmpty { + guard errorsToBeReported.isEmpty else { throw errorsToBeReported } } diff --git a/Plugins/PluginsShared/PluginError.swift b/Plugins/PluginsShared/PluginError.swift index ec6156ef..eb00d152 100644 --- a/Plugins/PluginsShared/PluginError.swift +++ b/Plugins/PluginsShared/PluginError.swift @@ -9,8 +9,7 @@ enum PluginError: Swift.Error, CustomStringConvertible, LocalizedError { var description: String { switch self { case .incompatibleTarget(let targetName): - return - "Incompatible target called '\(targetName)'. Only Swift source targets can be used with the Swift OpenAPI generator plugin." + return "Incompatible target called '\(targetName)'. Only Swift source targets can be used with the Swift OpenAPI generator plugin." case .noTargetsFoundForCommandPlugin: return "None of the targets include valid OpenAPI spec files. Please make sure at least one of your targets has any valid OpenAPI spec files before triggering this command plugin. See documentation for details." case .fileErrors(let errors, let targetName): @@ -54,20 +53,16 @@ struct FileError: Swift.Error, CustomStringConvertible, LocalizedError { case .config: switch issue { case .notFound: - return - "No config file found in the target named '\(targetName)'. Add a file called 'openapi-generator-config.yaml' or 'openapi-generator-config.yml' to the target's source directory. See documentation for details." + return "No config file found in the target named '\(targetName)'. Add a file called 'openapi-generator-config.yaml' or 'openapi-generator-config.yml' to the target's source directory. See documentation for details." case .multiFound(let files): - return - "Multiple config files found in the target named '\(targetName)', but exactly one is expected. Found \(files.map(\.description).joined(separator: " "))." + return "Multiple config files found in the target named '\(targetName)', but exactly one is expected. Found \(files.map(\.description).joined(separator: " "))." } case .document: switch issue { case .notFound: - return - "No OpenAPI document found in the target named '\(targetName)'. Add a file called 'openapi.yaml', 'openapi.yml' or 'openapi.json' (can also be a symlink) to the target's source directory. See documentation for details." + return "No OpenAPI document found in the target named '\(targetName)'. Add a file called 'openapi.yaml', 'openapi.yml' or 'openapi.json' (can also be a symlink) to the target's source directory. See documentation for details." case .multiFound(let files): - return - "Multiple OpenAPI documents found in the target named '\(targetName)', but exactly one is expected. Found \(files.map(\.description).joined(separator: " "))." + return "Multiple OpenAPI documents found in the target named '\(targetName)', but exactly one is expected. Found \(files.map(\.description).joined(separator: " "))." } } } From 07e3163d8456460edf7321d67e3c854c523b6fae Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Sun, 16 Jul 2023 13:03:07 +0330 Subject: [PATCH 059/100] soundness --- Package.swift | 2 +- Plugins/OpenAPIGeneratorCommand/plugin.swift | 5 ++--- Plugins/PluginsShared/PluginUtils.swift | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Package.swift b/Package.swift index d74166d0..ab672d96 100644 --- a/Package.swift +++ b/Package.swift @@ -160,7 +160,7 @@ let package = Package( permissions: [.writeToPackageDirectory(reason: "To write the generated Swift files back into the source directory of the package.")] ), dependencies: [ - "swift-openapi-generator", + "swift-openapi-generator" ] ) ] diff --git a/Plugins/OpenAPIGeneratorCommand/plugin.swift b/Plugins/OpenAPIGeneratorCommand/plugin.swift index d73ac4bb..e2b569c2 100644 --- a/Plugins/OpenAPIGeneratorCommand/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommand/plugin.swift @@ -114,15 +114,14 @@ extension SwiftOpenAPIGeneratorPlugin { } switch error { case .fileErrors(let errors, _): - if errors.count != FileError.Kind.allCases.count { + guard errors.count == FileError.Kind.allCases.count else { // There are some file-finding errors but at least 1 file is available. // This means the user means to use the target with the generator, just // hasn't configured their target properly. // We'll throw this error to let them know. return error - } else { - return nil } + return nil case .incompatibleTarget, .noTargetsFoundForCommandPlugin: // We can't throw any of these errors because they only complain about // the target not being openapi-generator compatible. diff --git a/Plugins/PluginsShared/PluginUtils.swift b/Plugins/PluginsShared/PluginUtils.swift index 95f32d4c..897e9839 100644 --- a/Plugins/PluginsShared/PluginUtils.swift +++ b/Plugins/PluginsShared/PluginUtils.swift @@ -26,7 +26,7 @@ enum PluginUtils { "generate", "\(doc)", "--config", "\(config)", "--output-directory", "\(genSourcesDir)", - "--invoked-from", "\(invocationSource.rawValue)" + "--invoked-from", "\(invocationSource.rawValue)", ] let tool = try tool("swift-openapi-generator") From c72516c88dc463586f299324c04e9a09f03ae6e3 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Sun, 16 Jul 2023 13:05:09 +0330 Subject: [PATCH 060/100] soundness --- Plugins/PluginsShared/PluginUtils.swift | 48 ++++++++++++++----------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/Plugins/PluginsShared/PluginUtils.swift b/Plugins/PluginsShared/PluginUtils.swift index 897e9839..c5fd8a2e 100644 --- a/Plugins/PluginsShared/PluginUtils.swift +++ b/Plugins/PluginsShared/PluginUtils.swift @@ -64,18 +64,22 @@ enum PluginUtils { ) -> Result { let matchedConfigs = inputFiles.filter { supportedConfigFiles.contains($0.path.lastComponent) }.map(\.path) guard matchedConfigs.count > 0 else { - return .failure(FileError( - targetName: targetName, - fileKind: .config, - issue: .notFound - )) + return .failure( + FileError( + targetName: targetName, + fileKind: .config, + issue: .notFound + ) + ) } guard matchedConfigs.count == 1 else { - return .failure(FileError( - targetName: targetName, - fileKind: .config, - issue: .multiFound(files: matchedConfigs) - )) + return .failure( + FileError( + targetName: targetName, + fileKind: .config, + issue: .multiFound(files: matchedConfigs) + ) + ) } return .success(matchedConfigs[0]) } @@ -86,18 +90,22 @@ enum PluginUtils { ) -> Result { let matchedDocs = inputFiles.filter { supportedDocFiles.contains($0.path.lastComponent) }.map(\.path) guard matchedDocs.count > 0 else { - return .failure(FileError( - targetName: targetName, - fileKind: .document, - issue: .notFound - )) + return .failure( + FileError( + targetName: targetName, + fileKind: .document, + issue: .notFound + ) + ) } guard matchedDocs.count == 1 else { - return .failure(FileError( - targetName: targetName, - fileKind: .document, - issue: .multiFound(files: matchedDocs) - )) + return .failure( + FileError( + targetName: targetName, + fileKind: .document, + issue: .multiFound(files: matchedDocs) + ) + ) } return .success(matchedDocs[0]) } From 379abff6f1c3e576121b05836d3336dcf025d578 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Sun, 16 Jul 2023 13:12:48 +0330 Subject: [PATCH 061/100] more soundness --- Plugins/PluginsShared/PluginUtils.swift | 2 +- .../InvocationSource.swift | 1 - .../swift-openapi-generator/runGenerator.swift | 17 ++++++++--------- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/Plugins/PluginsShared/PluginUtils.swift b/Plugins/PluginsShared/PluginUtils.swift index c5fd8a2e..db5d0004 100644 --- a/Plugins/PluginsShared/PluginUtils.swift +++ b/Plugins/PluginsShared/PluginUtils.swift @@ -20,7 +20,7 @@ enum PluginUtils { invocationSource: InvocationSource ) throws -> ValidatedInputs { let (config, doc) = try findFiles(inputFiles: sourceFiles, targetName: targetName) - let genSourcesDir = workingDirectory.appending("GeneratedSources") + let genSourcesDir = workingDirectory.appending("OpenAPISources") let arguments = [ "generate", "\(doc)", diff --git a/Sources/swift-openapi-generator/InvocationSource.swift b/Sources/swift-openapi-generator/InvocationSource.swift index 6614ec7b..6226d8cd 100644 --- a/Sources/swift-openapi-generator/InvocationSource.swift +++ b/Sources/swift-openapi-generator/InvocationSource.swift @@ -1,4 +1,3 @@ - enum InvocationSource: String, Codable { case BuildToolPlugin case CommandPlugin diff --git a/Sources/swift-openapi-generator/runGenerator.swift b/Sources/swift-openapi-generator/runGenerator.swift index bc2f11e4..b9fe079b 100644 --- a/Sources/swift-openapi-generator/runGenerator.swift +++ b/Sources/swift-openapi-generator/runGenerator.swift @@ -123,16 +123,15 @@ extension _Tool { let path = outputDirectory.appendingPathComponent(fileName) let data = try contents() - if fm.fileExists(atPath: path.path) { - let existingData = try? Data(contentsOf: path) - if existingData == data { - return false - } else { - try data.write(to: path) - return true - } - } else { + guard fm.fileExists(atPath: path.path) else { return fm.createFile(atPath: path.path, contents: data) } + let existingData = try? Data(contentsOf: path) + if existingData == data { + return false + } else { + try data.write(to: path) + return true + } } } From 62dba757d01d141063a95636274c7f3b3e69d1f7 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Mon, 17 Jul 2023 07:19:01 +0330 Subject: [PATCH 062/100] repair/add comments --- Plugins/PluginsShared/PluginUtils.swift | 6 ++++++ .../GenerateOptions+runGenerator.swift | 7 +------ .../InvocationSource.swift | 1 + .../swift-openapi-generator/UserConfig.swift | 2 +- .../runGenerator.swift | 21 +++++++++---------- 5 files changed, 19 insertions(+), 18 deletions(-) diff --git a/Plugins/PluginsShared/PluginUtils.swift b/Plugins/PluginsShared/PluginUtils.swift index db5d0004..15df1597 100644 --- a/Plugins/PluginsShared/PluginUtils.swift +++ b/Plugins/PluginsShared/PluginUtils.swift @@ -4,6 +4,7 @@ enum PluginUtils { private static var supportedConfigFiles: Set { Set(["yaml", "yml"].map { "openapi-generator-config." + $0 }) } private static var supportedDocFiles: Set { Set(["yaml", "yml", "json"].map { "openapi." + $0 }) } + /// Validated values to run a plugin with. struct ValidatedInputs { let doc: Path let config: Path @@ -12,6 +13,7 @@ enum PluginUtils { let tool: PluginContext.Tool } + /// Validates the inputs and returns the necessary values to run a plugin. static func validateInputs( workingDirectory: Path, tool: (String) throws -> PluginContext.Tool, @@ -40,6 +42,8 @@ enum PluginUtils { ) } + /// Finds the OpenAPI config and document files or throws an error including both possible + /// previous errors from the process of finding the config and document files. private static func findFiles( inputFiles: FileList, targetName: String @@ -58,6 +62,7 @@ enum PluginUtils { } } + /// Find the config file. private static func findConfig( inputFiles: FileList, targetName: String @@ -84,6 +89,7 @@ enum PluginUtils { return .success(matchedConfigs[0]) } + /// Find the document file. private static func findDocument( inputFiles: FileList, targetName: String diff --git a/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift b/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift index 339f7754..08f82d62 100644 --- a/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift +++ b/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift @@ -21,12 +21,7 @@ extension _GenerateOptions { /// - Parameters: /// - outputDirectory: The directory path to which the generator writes /// the generated Swift files. - /// - isPluginInvocation: A Boolean value that indicates whether this - /// generator invocation is coming from a SwiftPM plugin, as that forces - /// the generator to emit all 3 files (Types.swift, Client.Swift, and - /// Server.swift) regardless of which generator mode was requested, with - /// the caveat that the not requested files are empty. This is due to - /// a limitation of the build system used by SwiftPM under the hood. + /// - invocationSource: The source of the generator invocation. func runGenerator( outputDirectory: URL, invocationSource: InvocationSource diff --git a/Sources/swift-openapi-generator/InvocationSource.swift b/Sources/swift-openapi-generator/InvocationSource.swift index 6226d8cd..a3947c78 100644 --- a/Sources/swift-openapi-generator/InvocationSource.swift +++ b/Sources/swift-openapi-generator/InvocationSource.swift @@ -1,3 +1,4 @@ +/// The source of the generator invocation. enum InvocationSource: String, Codable { case BuildToolPlugin case CommandPlugin diff --git a/Sources/swift-openapi-generator/UserConfig.swift b/Sources/swift-openapi-generator/UserConfig.swift index db198f6f..e2267680 100644 --- a/Sources/swift-openapi-generator/UserConfig.swift +++ b/Sources/swift-openapi-generator/UserConfig.swift @@ -19,7 +19,7 @@ import _OpenAPIGeneratorCore /// of the generator pipeline, and, for example, generate both Types.swift and /// Client.swift in one invocation of the command-line tool. struct _UserConfig: Codable { - + /// A list of modes to use, in other words, which Swift files to generate. var generate: [GeneratorMode] diff --git a/Sources/swift-openapi-generator/runGenerator.swift b/Sources/swift-openapi-generator/runGenerator.swift index b9fe079b..568f929a 100644 --- a/Sources/swift-openapi-generator/runGenerator.swift +++ b/Sources/swift-openapi-generator/runGenerator.swift @@ -21,8 +21,7 @@ extension _Tool { /// - Parameters: /// - doc: A path to the OpenAPI document. /// - configs: A list of generator configurations. - /// - isPluginInvocation: A Boolean value that indicates whether this - /// generator invocation is coming from a SwiftPM plugin. + /// - invocationSource: The source of the generator invocation. /// - outputDirectory: The directory to which the generator writes /// the generated Swift files. /// - diagnostics: A collector for diagnostics emitted by the generator. @@ -40,10 +39,6 @@ extension _Tool { throw ValidationError("Failed to load the OpenAPI document at path \(doc.path), error: \(error)") } for config in configs { - // BuildTool plugins guarantee the compiler that they will create some files - // with specific names (the same as `config.mode.outputFileNameSuffix`, for us) - // To make sure the BuildTool plugin and the Command plugin don't create files with - // the same names, we add a prefix to the names. try runGenerator( doc: doc, docData: docData, @@ -54,8 +49,10 @@ extension _Tool { ) } - // Swift expects us to always create these files in BuildTool plugins, - // so we create the unused files, but empty. + // If from a BuildTool plugin, the generator will have to emit all 3 files + // (Types.swift, Client.Swift, and Server.swift) regardless of which generator + // mode was requested, with the caveat that the not-requested files are empty. + // This is due to a limitation of the build system used by SwiftPM under the hood. if invocationSource == .BuildToolPlugin { let nonGeneratedModes = Set(GeneratorMode.allCases).subtracting(configs.map(\.mode)) for mode in nonGeneratedModes.sorted() { @@ -73,8 +70,10 @@ extension _Tool { /// - doc: A path to the OpenAPI document. /// - docData: The raw contents of the OpenAPI document. /// - config: A set of configuration values for the generator. - /// - outputFilePath: The directory to which the generator writes - /// the generated Swift files. + /// - outputDirectory: The directory to which the generator writes + /// the generated Swift file. + /// - outputFileName: The file name to which the generator writes + /// the generated Swift file. /// - diagnostics: A collector for diagnostics emitted by the generator. static func runGenerator( doc: URL, @@ -113,7 +112,7 @@ extension _Tool { ) throws -> Bool { let fm = FileManager.default - // Create directory if doesn't exist + // Create directory if it doesn't exist. if !fm.fileExists(atPath: outputDirectory.path) { try fm.createDirectory( at: outputDirectory, From d9282d608b0344ccbc88a6cbcede96518c0b8cfb Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Mon, 17 Jul 2023 07:25:51 +0330 Subject: [PATCH 063/100] better FileError --- Plugins/PluginsShared/PluginError.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Plugins/PluginsShared/PluginError.swift b/Plugins/PluginsShared/PluginError.swift index eb00d152..c90f53ad 100644 --- a/Plugins/PluginsShared/PluginError.swift +++ b/Plugins/PluginsShared/PluginError.swift @@ -45,10 +45,10 @@ struct FileError: Swift.Error, CustomStringConvertible, LocalizedError { let issue: Issue var description: String { - "FileError { targetName: \(targetName), fileKind: \(fileKind), description: \(preciseErrorDescription) }" + "FileError { fileKind: \(fileKind), description: \(helpAnchor!) }" } - var preciseErrorDescription: String { + var helpAnchor: String? { switch fileKind { case .config: switch issue { From f7a834c22c8a91d51fb6b41baeea69a75c0a2034 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Mon, 17 Jul 2023 07:35:13 +0330 Subject: [PATCH 064/100] more refinements --- Plugins/OpenAPIGeneratorCommand/plugin.swift | 13 ++++++------- Plugins/PluginsShared/PluginError.swift | 9 +++++++++ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/Plugins/OpenAPIGeneratorCommand/plugin.swift b/Plugins/OpenAPIGeneratorCommand/plugin.swift index e2b569c2..0b101722 100644 --- a/Plugins/OpenAPIGeneratorCommand/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommand/plugin.swift @@ -114,14 +114,13 @@ extension SwiftOpenAPIGeneratorPlugin { } switch error { case .fileErrors(let errors, _): - guard errors.count == FileError.Kind.allCases.count else { - // There are some file-finding errors but at least 1 file is available. - // This means the user means to use the target with the generator, just - // hasn't configured their target properly. - // We'll throw this error to let them know. - return error + if errors.count == FileError.Kind.allCases.count, + errors.allSatisfy(\.issue.isNotFound) { + // No files were found so there is no indication that the target is supposed + // to be generator-compatible. + return nil } - return nil + return error case .incompatibleTarget, .noTargetsFoundForCommandPlugin: // We can't throw any of these errors because they only complain about // the target not being openapi-generator compatible. diff --git a/Plugins/PluginsShared/PluginError.swift b/Plugins/PluginsShared/PluginError.swift index c90f53ad..486e0368 100644 --- a/Plugins/PluginsShared/PluginError.swift +++ b/Plugins/PluginsShared/PluginError.swift @@ -38,6 +38,15 @@ struct FileError: Swift.Error, CustomStringConvertible, LocalizedError { enum Issue { case notFound case multiFound(files: [Path]) + + var isNotFound: Bool { + switch self { + case .notFound: + return true + case .multiFound: + return false + } + } } let targetName: String From 691776a55ebf854b71751f21865800427cb6d9c2 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Mon, 17 Jul 2023 07:47:01 +0330 Subject: [PATCH 065/100] soundness --- Plugins/OpenAPIGeneratorCommand/plugin.swift | 3 ++- Sources/swift-openapi-generator/GenerateCommand.swift | 3 ++- Sources/swift-openapi-generator/runGenerator.swift | 5 ++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Plugins/OpenAPIGeneratorCommand/plugin.swift b/Plugins/OpenAPIGeneratorCommand/plugin.swift index 0b101722..708a8aa6 100644 --- a/Plugins/OpenAPIGeneratorCommand/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommand/plugin.swift @@ -115,7 +115,8 @@ extension SwiftOpenAPIGeneratorPlugin { switch error { case .fileErrors(let errors, _): if errors.count == FileError.Kind.allCases.count, - errors.allSatisfy(\.issue.isNotFound) { + errors.allSatisfy(\.issue.isNotFound) + { // No files were found so there is no indication that the target is supposed // to be generator-compatible. return nil diff --git a/Sources/swift-openapi-generator/GenerateCommand.swift b/Sources/swift-openapi-generator/GenerateCommand.swift index 1bcf3c64..26b864e9 100644 --- a/Sources/swift-openapi-generator/GenerateCommand.swift +++ b/Sources/swift-openapi-generator/GenerateCommand.swift @@ -56,4 +56,5 @@ struct _GenerateCommand: AsyncParsableCommand { } } -extension InvocationSource: ExpressibleByArgument { } +// MARK: - InvocationSource + ExpressibleByArgument +extension InvocationSource: ExpressibleByArgument {} diff --git a/Sources/swift-openapi-generator/runGenerator.swift b/Sources/swift-openapi-generator/runGenerator.swift index 568f929a..ebf7e86e 100644 --- a/Sources/swift-openapi-generator/runGenerator.swift +++ b/Sources/swift-openapi-generator/runGenerator.swift @@ -126,11 +126,10 @@ extension _Tool { return fm.createFile(atPath: path.path, contents: data) } let existingData = try? Data(contentsOf: path) - if existingData == data { - return false - } else { + guard existingData == data else { try data.write(to: path) return true } + return false } } From cb70f29d3e1370a4af6b6642c36a3c5cb8fe3ec7 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Mon, 17 Jul 2023 08:22:00 +0330 Subject: [PATCH 066/100] FileError exclude unneeded info for conciseness --- Plugins/PluginsShared/PluginError.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/PluginsShared/PluginError.swift b/Plugins/PluginsShared/PluginError.swift index 486e0368..df513447 100644 --- a/Plugins/PluginsShared/PluginError.swift +++ b/Plugins/PluginsShared/PluginError.swift @@ -54,7 +54,7 @@ struct FileError: Swift.Error, CustomStringConvertible, LocalizedError { let issue: Issue var description: String { - "FileError { fileKind: \(fileKind), description: \(helpAnchor!) }" + "FileError { \(helpAnchor!) }" } var helpAnchor: String? { From 48120d51cd17fcd7790d7e38b77e4e61deeca20f Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Mon, 17 Jul 2023 12:57:34 +0330 Subject: [PATCH 067/100] Update Plugins/PluginsShared/PluginError.swift Co-authored-by: Honza Dvorsky --- Plugins/PluginsShared/PluginError.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/PluginsShared/PluginError.swift b/Plugins/PluginsShared/PluginError.swift index df513447..4f0ed416 100644 --- a/Plugins/PluginsShared/PluginError.swift +++ b/Plugins/PluginsShared/PluginError.swift @@ -9,7 +9,7 @@ enum PluginError: Swift.Error, CustomStringConvertible, LocalizedError { var description: String { switch self { case .incompatibleTarget(let targetName): - return "Incompatible target called '\(targetName)'. Only Swift source targets can be used with the Swift OpenAPI generator plugin." + return "Incompatible target called '\(targetName)'. Only Swift source targets can be used with the Swift OpenAPI Generator plugin." case .noTargetsFoundForCommandPlugin: return "None of the targets include valid OpenAPI spec files. Please make sure at least one of your targets has any valid OpenAPI spec files before triggering this command plugin. See documentation for details." case .fileErrors(let errors, let targetName): From 125d5b535507a9e7692791d9f84bd642371c4ffb Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Mon, 17 Jul 2023 13:26:21 +0330 Subject: [PATCH 068/100] apply some suggestions --- Plugins/OpenAPIGenerator/plugin.swift | 2 +- Plugins/OpenAPIGeneratorCommand/plugin.swift | 4 +++- Plugins/PluginsShared/InvocationSource.swift | 1 - Plugins/PluginsShared/PluginError.swift | 18 +++++++++--------- Plugins/PluginsShared/PluginSource.swift | 1 + Plugins/PluginsShared/PluginUtils.swift | 12 ++++++------ .../GenerateCommand.swift | 6 +++--- .../GenerateOptions+runGenerator.swift | 8 ++++---- .../InvocationSource.swift | 6 ------ .../swift-openapi-generator/PluginSource.swift | 5 +++++ .../swift-openapi-generator/runGenerator.swift | 6 +++--- 11 files changed, 35 insertions(+), 34 deletions(-) delete mode 120000 Plugins/PluginsShared/InvocationSource.swift create mode 120000 Plugins/PluginsShared/PluginSource.swift delete mode 100644 Sources/swift-openapi-generator/InvocationSource.swift create mode 100644 Sources/swift-openapi-generator/PluginSource.swift diff --git a/Plugins/OpenAPIGenerator/plugin.swift b/Plugins/OpenAPIGenerator/plugin.swift index eb60b08d..98744ef1 100644 --- a/Plugins/OpenAPIGenerator/plugin.swift +++ b/Plugins/OpenAPIGenerator/plugin.swift @@ -27,7 +27,7 @@ struct SwiftOpenAPIGeneratorPlugin { tool: tool, sourceFiles: sourceFiles, targetName: targetName, - invocationSource: .BuildToolPlugin + pluginSource: .build ) let outputFiles: [Path] = GeneratorMode.allCases.map { inputs.genSourcesDir.appending($0.outputFileName) } diff --git a/Plugins/OpenAPIGeneratorCommand/plugin.swift b/Plugins/OpenAPIGeneratorCommand/plugin.swift index 708a8aa6..674c0668 100644 --- a/Plugins/OpenAPIGeneratorCommand/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommand/plugin.swift @@ -27,14 +27,16 @@ struct SwiftOpenAPIGeneratorPlugin { tool: tool, sourceFiles: sourceFiles, targetName: targetName, - invocationSource: .CommandPlugin + pluginSource: .command ) let toolUrl = URL(fileURLWithPath: inputs.tool.path.string) let process = Process() process.executableURL = toolUrl process.arguments = inputs.arguments + process.environment = [:] try process.run() + process.waitUntilExit() } } diff --git a/Plugins/PluginsShared/InvocationSource.swift b/Plugins/PluginsShared/InvocationSource.swift deleted file mode 120000 index a48bb368..00000000 --- a/Plugins/PluginsShared/InvocationSource.swift +++ /dev/null @@ -1 +0,0 @@ -../../Sources/swift-openapi-generator/InvocationSource.swift \ No newline at end of file diff --git a/Plugins/PluginsShared/PluginError.swift b/Plugins/PluginsShared/PluginError.swift index 4f0ed416..80dc8971 100644 --- a/Plugins/PluginsShared/PluginError.swift +++ b/Plugins/PluginsShared/PluginError.swift @@ -13,7 +13,7 @@ enum PluginError: Swift.Error, CustomStringConvertible, LocalizedError { case .noTargetsFoundForCommandPlugin: return "None of the targets include valid OpenAPI spec files. Please make sure at least one of your targets has any valid OpenAPI spec files before triggering this command plugin. See documentation for details." case .fileErrors(let errors, let targetName): - return "Found file errors in target called '\(targetName)': \(errors.description)" + return "Found file errors in target called '\(targetName)': \(errors)" } } @@ -36,14 +36,14 @@ struct FileError: Swift.Error, CustomStringConvertible, LocalizedError { } enum Issue { - case notFound - case multiFound(files: [Path]) + case noFilesFound + case multipleFilesFound(files: [Path]) var isNotFound: Bool { switch self { - case .notFound: + case .noFilesFound: return true - case .multiFound: + case .multipleFilesFound: return false } } @@ -61,16 +61,16 @@ struct FileError: Swift.Error, CustomStringConvertible, LocalizedError { switch fileKind { case .config: switch issue { - case .notFound: + case .noFilesFound: return "No config file found in the target named '\(targetName)'. Add a file called 'openapi-generator-config.yaml' or 'openapi-generator-config.yml' to the target's source directory. See documentation for details." - case .multiFound(let files): + case .multipleFilesFound(let files): return "Multiple config files found in the target named '\(targetName)', but exactly one is expected. Found \(files.map(\.description).joined(separator: " "))." } case .document: switch issue { - case .notFound: + case .noFilesFound: return "No OpenAPI document found in the target named '\(targetName)'. Add a file called 'openapi.yaml', 'openapi.yml' or 'openapi.json' (can also be a symlink) to the target's source directory. See documentation for details." - case .multiFound(let files): + case .multipleFilesFound(let files): return "Multiple OpenAPI documents found in the target named '\(targetName)', but exactly one is expected. Found \(files.map(\.description).joined(separator: " "))." } } diff --git a/Plugins/PluginsShared/PluginSource.swift b/Plugins/PluginsShared/PluginSource.swift new file mode 120000 index 00000000..fef39cd8 --- /dev/null +++ b/Plugins/PluginsShared/PluginSource.swift @@ -0,0 +1 @@ +../../Sources/swift-openapi-generator/PluginSource.swift \ No newline at end of file diff --git a/Plugins/PluginsShared/PluginUtils.swift b/Plugins/PluginsShared/PluginUtils.swift index 15df1597..18d6cb4f 100644 --- a/Plugins/PluginsShared/PluginUtils.swift +++ b/Plugins/PluginsShared/PluginUtils.swift @@ -19,7 +19,7 @@ enum PluginUtils { tool: (String) throws -> PluginContext.Tool, sourceFiles: FileList, targetName: String, - invocationSource: InvocationSource + pluginSource: PluginSource ) throws -> ValidatedInputs { let (config, doc) = try findFiles(inputFiles: sourceFiles, targetName: targetName) let genSourcesDir = workingDirectory.appending("OpenAPISources") @@ -28,7 +28,7 @@ enum PluginUtils { "generate", "\(doc)", "--config", "\(config)", "--output-directory", "\(genSourcesDir)", - "--invoked-from", "\(invocationSource.rawValue)", + "--invoked-from", "\(pluginSource.rawValue)", ] let tool = try tool("swift-openapi-generator") @@ -73,7 +73,7 @@ enum PluginUtils { FileError( targetName: targetName, fileKind: .config, - issue: .notFound + issue: .noFilesFound ) ) } @@ -82,7 +82,7 @@ enum PluginUtils { FileError( targetName: targetName, fileKind: .config, - issue: .multiFound(files: matchedConfigs) + issue: .multipleFilesFound(files: matchedConfigs) ) ) } @@ -100,7 +100,7 @@ enum PluginUtils { FileError( targetName: targetName, fileKind: .document, - issue: .notFound + issue: .noFilesFound ) ) } @@ -109,7 +109,7 @@ enum PluginUtils { FileError( targetName: targetName, fileKind: .document, - issue: .multiFound(files: matchedDocs) + issue: .multipleFilesFound(files: matchedDocs) ) ) } diff --git a/Sources/swift-openapi-generator/GenerateCommand.swift b/Sources/swift-openapi-generator/GenerateCommand.swift index 26b864e9..43845412 100644 --- a/Sources/swift-openapi-generator/GenerateCommand.swift +++ b/Sources/swift-openapi-generator/GenerateCommand.swift @@ -46,15 +46,15 @@ struct _GenerateCommand: AsyncParsableCommand { help: "Whether this invocation is from the SwiftPM plugin. We always need to produce all files when invoked from the plugin. Non-requested modes produce empty files." ) - var invokedFrom: InvocationSource = .CLI + var invokedFrom: PluginSource? func run() async throws { try generate.runGenerator( outputDirectory: outputDirectory, - invocationSource: invokedFrom + pluginSource: invokedFrom ) } } // MARK: - InvocationSource + ExpressibleByArgument -extension InvocationSource: ExpressibleByArgument {} +extension PluginSource: ExpressibleByArgument {} diff --git a/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift b/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift index 08f82d62..8d6571fa 100644 --- a/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift +++ b/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift @@ -21,10 +21,10 @@ extension _GenerateOptions { /// - Parameters: /// - outputDirectory: The directory path to which the generator writes /// the generated Swift files. - /// - invocationSource: The source of the generator invocation. + /// - pluginSource: The source of the generator invocation if from a plugin. func runGenerator( outputDirectory: URL, - invocationSource: InvocationSource + pluginSource: PluginSource? ) throws { let config = try loadedConfig() let sortedModes = try resolvedModes(config) @@ -57,7 +57,7 @@ extension _GenerateOptions { - Output directory: \(outputDirectory.path) - Diagnostics output path: \(diagnosticsOutputPath?.path ?? "") - Current directory: \(FileManager.default.currentDirectoryPath) - - Invoked from: \(invocationSource.rawValue) + - Plugin invoked from: \(pluginSource?.rawValue ?? "") - Additional imports: \(resolvedAdditionalImports.isEmpty ? "" : resolvedAdditionalImports.joined(separator: ", ")) """ ) @@ -65,7 +65,7 @@ extension _GenerateOptions { try _Tool.runGenerator( doc: doc, configs: configs, - invocationSource: invocationSource, + pluginSource: pluginSource, outputDirectory: outputDirectory, diagnostics: diagnostics ) diff --git a/Sources/swift-openapi-generator/InvocationSource.swift b/Sources/swift-openapi-generator/InvocationSource.swift deleted file mode 100644 index a3947c78..00000000 --- a/Sources/swift-openapi-generator/InvocationSource.swift +++ /dev/null @@ -1,6 +0,0 @@ -/// The source of the generator invocation. -enum InvocationSource: String, Codable { - case BuildToolPlugin - case CommandPlugin - case CLI -} diff --git a/Sources/swift-openapi-generator/PluginSource.swift b/Sources/swift-openapi-generator/PluginSource.swift new file mode 100644 index 00000000..b7fd8722 --- /dev/null +++ b/Sources/swift-openapi-generator/PluginSource.swift @@ -0,0 +1,5 @@ +/// The source of a plugin generator invocation. +enum PluginSource: String, Codable { + case build + case command +} diff --git a/Sources/swift-openapi-generator/runGenerator.swift b/Sources/swift-openapi-generator/runGenerator.swift index ebf7e86e..0ccd9a8f 100644 --- a/Sources/swift-openapi-generator/runGenerator.swift +++ b/Sources/swift-openapi-generator/runGenerator.swift @@ -21,14 +21,14 @@ extension _Tool { /// - Parameters: /// - doc: A path to the OpenAPI document. /// - configs: A list of generator configurations. - /// - invocationSource: The source of the generator invocation. + /// - pluginSource: The source of the generator invocation. /// - outputDirectory: The directory to which the generator writes /// the generated Swift files. /// - diagnostics: A collector for diagnostics emitted by the generator. static func runGenerator( doc: URL, configs: [Config], - invocationSource: InvocationSource, + pluginSource: PluginSource?, outputDirectory: URL, diagnostics: any DiagnosticCollector ) throws { @@ -53,7 +53,7 @@ extension _Tool { // (Types.swift, Client.Swift, and Server.swift) regardless of which generator // mode was requested, with the caveat that the not-requested files are empty. // This is due to a limitation of the build system used by SwiftPM under the hood. - if invocationSource == .BuildToolPlugin { + if pluginSource == .build { let nonGeneratedModes = Set(GeneratorMode.allCases).subtracting(configs.map(\.mode)) for mode in nonGeneratedModes.sorted() { try replaceFileContents( From 5dbc8e51507b44bc64a53a8d65546376062d1d6c Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Mon, 17 Jul 2023 13:40:38 +0330 Subject: [PATCH 069/100] Update PluginUtils.swift --- Plugins/PluginsShared/PluginUtils.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/PluginsShared/PluginUtils.swift b/Plugins/PluginsShared/PluginUtils.swift index 18d6cb4f..b9022e78 100644 --- a/Plugins/PluginsShared/PluginUtils.swift +++ b/Plugins/PluginsShared/PluginUtils.swift @@ -22,7 +22,7 @@ enum PluginUtils { pluginSource: PluginSource ) throws -> ValidatedInputs { let (config, doc) = try findFiles(inputFiles: sourceFiles, targetName: targetName) - let genSourcesDir = workingDirectory.appending("OpenAPISources") + let genSourcesDir = workingDirectory.appending("GeneratedSources") let arguments = [ "generate", "\(doc)", From fe01b88a762435ff66284dfb77af77420a57aa37 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Tue, 18 Jul 2023 13:15:33 +0330 Subject: [PATCH 070/100] get it working with passing only 1 target --- Plugins/OpenAPIGeneratorCommand/plugin.swift | 116 ++++++++----------- Plugins/PluginsShared/PluginError.swift | 23 ++-- 2 files changed, 59 insertions(+), 80 deletions(-) diff --git a/Plugins/OpenAPIGeneratorCommand/plugin.swift b/Plugins/OpenAPIGeneratorCommand/plugin.swift index 674c0668..6667e83d 100644 --- a/Plugins/OpenAPIGeneratorCommand/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommand/plugin.swift @@ -45,27 +45,28 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { context: PluginContext, arguments: [String] ) async throws { - var hasHadASuccessfulRun = false - var errors = [(error: any Error, targetName: String)]() - for target in context.package.targets { + let targetName = try parseTargetName(from: arguments) + let matchingTargets = try context.package.targets(named: [targetName]) + + switch matchingTargets.count { + case 0: + throw PluginError.noTargetsMatchingTargetName(targetName: targetName) + case 1: + let target = matchingTargets[0] guard let swiftTarget = target as? SwiftSourceModuleTarget else { - continue - } - do { - try runCommand( - targetWorkingDirectory: target.directory, - tool: context.tool, - sourceFiles: swiftTarget.sourceFiles, - targetName: target.name - ) - hasHadASuccessfulRun = true - } catch { - errors.append((error, target.name)) + throw PluginError.incompatibleTarget(targetName: target.name) } - } - try throwErrorsIfNecessary(errors) - guard hasHadASuccessfulRun else { - throw PluginError.noTargetsFoundForCommandPlugin + try runCommand( + targetWorkingDirectory: target.directory, + tool: context.tool, + sourceFiles: swiftTarget.sourceFiles, + targetName: target.name + ) + default: + throw PluginError.tooManyTargetsMatchingTargetName( + targetName: targetName, + matchingTargetNames: matchingTargets.map(\.name) + ) } } } @@ -78,62 +79,43 @@ extension SwiftOpenAPIGeneratorPlugin: XcodeCommandPlugin { context: XcodePluginContext, arguments: [String] ) throws { - var hasHadASuccessfulRun = false - var errors = [(error: any Error, targetName: String)]() - for xcodeTarget in context.xcodeProject.targets { + let targetName = try parseTargetName(from: arguments) + let matchingTargets = context.xcodeProject.targets.filter { + $0.displayName == targetName + } + + switch matchingTargets.count { + case 0: + throw PluginError.noTargetsMatchingTargetName(targetName: targetName) + case 1: + let xcodeTarget = matchingTargets[0] guard let target = xcodeTarget as? SourceModuleTarget else { - continue - } - do { - try runCommand( - targetWorkingDirectory: target.directory, - tool: context.tool, - sourceFiles: xcodeTarget.inputFiles, - targetName: xcodeTarget.displayName - ) - hasHadASuccessfulRun = true - } catch { - errors.append((error, target.name)) + throw PluginError.incompatibleTarget(targetName: xcodeTarget.displayName) } - } - try throwErrorsIfNecessary(errors) - guard hasHadASuccessfulRun else { - throw PluginError.noTargetsFoundForCommandPlugin + try runCommand( + targetWorkingDirectory: target.directory, + tool: context.tool, + sourceFiles: xcodeTarget.inputFiles, + targetName: xcodeTarget.displayName + ) + default: + throw PluginError.tooManyTargetsMatchingTargetName( + targetName: targetName, + matchingTargetNames: matchingTargets.map(\.displayName) + ) } } } #endif extension SwiftOpenAPIGeneratorPlugin { - /// Throws if there are any errors that show a target is definitely trying to - /// have OpenAPI generator compatibility, but is failing to. - func throwErrorsIfNecessary(_ errors: [(error: any Error, targetName: String)]) throws { - let errorsToBeReported = errors.compactMap { (error, targetName) -> PluginError? in - guard let error = error as? PluginError else { - print("Unknown error reported by run command for target '\(targetName)'. This is unexpected and should not happen. Please report at https://github.com/apple/swift-openapi-generator/issues") - // Don't throw the error to not interrupt the process. - return nil - } - switch error { - case .fileErrors(let errors, _): - if errors.count == FileError.Kind.allCases.count, - errors.allSatisfy(\.issue.isNotFound) - { - // No files were found so there is no indication that the target is supposed - // to be generator-compatible. - return nil - } - return error - case .incompatibleTarget, .noTargetsFoundForCommandPlugin: - // We can't throw any of these errors because they only complain about - // the target not being openapi-generator compatible. - // We can't expect all targets to be OpenAPI compatible. - return nil - } - } - - guard errorsToBeReported.isEmpty else { - throw errorsToBeReported + /// Parses the target name from the arguments. + func parseTargetName(from arguments: [String]) throws -> String { + guard arguments.count == 2, + arguments[0] == "--target" + else { + throw PluginError.badArguments(arguments) } + return arguments[1] } } diff --git a/Plugins/PluginsShared/PluginError.swift b/Plugins/PluginsShared/PluginError.swift index 80dc8971..517c23ae 100644 --- a/Plugins/PluginsShared/PluginError.swift +++ b/Plugins/PluginsShared/PluginError.swift @@ -3,17 +3,23 @@ import Foundation enum PluginError: Swift.Error, CustomStringConvertible, LocalizedError { case incompatibleTarget(targetName: String) - case noTargetsFoundForCommandPlugin + case badArguments([String]) + case noTargetsMatchingTargetName(targetName: String) + case tooManyTargetsMatchingTargetName(targetName: String, matchingTargetNames: [String]) case fileErrors([FileError], targetName: String) var description: String { switch self { case .incompatibleTarget(let targetName): return "Incompatible target called '\(targetName)'. Only Swift source targets can be used with the Swift OpenAPI Generator plugin." - case .noTargetsFoundForCommandPlugin: - return "None of the targets include valid OpenAPI spec files. Please make sure at least one of your targets has any valid OpenAPI spec files before triggering this command plugin. See documentation for details." + case .badArguments(let arguments): + return "Unexpected arguments: \(arguments). On Xcode, use Xcode's command plugin UI to choose one specific target before hitting 'Run'. Otherwise make sure arguments are exactly of form '--target '." + case .noTargetsMatchingTargetName(let targetName): + return "Found no targets matching target name '\(targetName)'. Please make sure the target name argument leads to one and only one target." + case .tooManyTargetsMatchingTargetName(let targetName, let matchingTargetNames): + return "Found too many targets matching target name '\(targetName)': \(matchingTargetNames). Please make sure the target name argument leads to a unique target." case .fileErrors(let errors, let targetName): - return "Found file errors in target called '\(targetName)': \(errors)" + return "Found file errors in target called '\(targetName)': \(errors)." } } @@ -38,15 +44,6 @@ struct FileError: Swift.Error, CustomStringConvertible, LocalizedError { enum Issue { case noFilesFound case multipleFilesFound(files: [Path]) - - var isNotFound: Bool { - switch self { - case .noFilesFound: - return true - case .multipleFilesFound: - return false - } - } } let targetName: String From 6fc3ff055cf036c8786298d7aaf6d0e39c8fd428 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Tue, 18 Jul 2023 21:15:20 +0330 Subject: [PATCH 071/100] run on in-package dependencies too --- Plugins/OpenAPIGeneratorCommand/plugin.swift | 98 +++++++++----------- Plugins/PluginsShared/PluginError.swift | 38 ++++++++ 2 files changed, 80 insertions(+), 56 deletions(-) diff --git a/Plugins/OpenAPIGeneratorCommand/plugin.swift b/Plugins/OpenAPIGeneratorCommand/plugin.swift index 6667e83d..e8ab04a1 100644 --- a/Plugins/OpenAPIGeneratorCommand/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommand/plugin.swift @@ -45,77 +45,63 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { context: PluginContext, arguments: [String] ) async throws { - let targetName = try parseTargetName(from: arguments) + guard arguments.count == 2, + arguments[0] == "--target" + else { + throw PluginError.badArguments(arguments) + } + let targetName = arguments[1] + let matchingTargets = try context.package.targets(named: [targetName]) switch matchingTargets.count { case 0: throw PluginError.noTargetsMatchingTargetName(targetName: targetName) case 1: - let target = matchingTargets[0] - guard let swiftTarget = target as? SwiftSourceModuleTarget else { - throw PluginError.incompatibleTarget(targetName: target.name) + let mainTarget = matchingTargets[0] + guard mainTarget is SwiftSourceModuleTarget else { + throw PluginError.incompatibleTarget(targetName: mainTarget.name) } - try runCommand( - targetWorkingDirectory: target.directory, - tool: context.tool, - sourceFiles: swiftTarget.sourceFiles, - targetName: target.name - ) - default: - throw PluginError.tooManyTargetsMatchingTargetName( - targetName: targetName, - matchingTargetNames: matchingTargets.map(\.name) - ) - } - } -} + let allDependencies = mainTarget.recursiveTargetDependencies + let packageTargets = Set(context.package.targets.map(\.id)) + let dependenciesInPackage = allDependencies.filter { packageTargets.contains($0.id) } -#if canImport(XcodeProjectPlugin) -import XcodeProjectPlugin + var hadASuccessfulRun = false -extension SwiftOpenAPIGeneratorPlugin: XcodeCommandPlugin { - func performCommand( - context: XcodePluginContext, - arguments: [String] - ) throws { - let targetName = try parseTargetName(from: arguments) - let matchingTargets = context.xcodeProject.targets.filter { - $0.displayName == targetName - } + for target in [mainTarget] + dependenciesInPackage { + guard let swiftTarget = target as? SwiftSourceModuleTarget else { + continue + } + do { + try runCommand( + targetWorkingDirectory: target.directory, + tool: context.tool, + sourceFiles: swiftTarget.sourceFiles, + targetName: target.name + ) + hadASuccessfulRun = true + } catch let error as PluginError { + if error.isDefiniteMisconfigurationError { + throw error + } + } - switch matchingTargets.count { - case 0: - throw PluginError.noTargetsMatchingTargetName(targetName: targetName) - case 1: - let xcodeTarget = matchingTargets[0] - guard let target = xcodeTarget as? SourceModuleTarget else { - throw PluginError.incompatibleTarget(targetName: xcodeTarget.displayName) + guard hadASuccessfulRun else { + // None of the targets had a successful run and none had any definite + // indication of trying to be OpenAPI-generator compatible. + // To follow how the build command works, we throw file errors to let users + // know they need to add these files to their target. + throw PluginError.fileErrors( + FileError.notFoundFileErrors(forTarget: mainTarget.name), + targetName: mainTarget.name + ) + } } - try runCommand( - targetWorkingDirectory: target.directory, - tool: context.tool, - sourceFiles: xcodeTarget.inputFiles, - targetName: xcodeTarget.displayName - ) default: throw PluginError.tooManyTargetsMatchingTargetName( targetName: targetName, - matchingTargetNames: matchingTargets.map(\.displayName) + matchingTargetNames: matchingTargets.map(\.name) ) } } } -#endif - -extension SwiftOpenAPIGeneratorPlugin { - /// Parses the target name from the arguments. - func parseTargetName(from arguments: [String]) throws -> String { - guard arguments.count == 2, - arguments[0] == "--target" - else { - throw PluginError.badArguments(arguments) - } - return arguments[1] - } -} diff --git a/Plugins/PluginsShared/PluginError.swift b/Plugins/PluginsShared/PluginError.swift index 517c23ae..3b74f65c 100644 --- a/Plugins/PluginsShared/PluginError.swift +++ b/Plugins/PluginsShared/PluginError.swift @@ -8,6 +8,16 @@ enum PluginError: Swift.Error, CustomStringConvertible, LocalizedError { case tooManyTargetsMatchingTargetName(targetName: String, matchingTargetNames: [String]) case fileErrors([FileError], targetName: String) + /// The error is definitely due to misconfiguration of a target. + var isDefiniteMisconfigurationError: Bool { + switch self { + case .incompatibleTarget, .badArguments, .noTargetsMatchingTargetName, .tooManyTargetsMatchingTargetName: + return false + case .fileErrors(let errors, _): + return errors.isDefiniteMisconfigurationError + } + } + var description: String { switch self { case .incompatibleTarget(let targetName): @@ -44,6 +54,16 @@ struct FileError: Swift.Error, CustomStringConvertible, LocalizedError { enum Issue { case noFilesFound case multipleFilesFound(files: [Path]) + + /// The error is definitely due to misconfiguration of a target. + var isDefiniteMisconfigurationError: Bool { + switch self { + case .noFilesFound: + return false + case .multipleFilesFound: + return true + } + } } let targetName: String @@ -76,4 +96,22 @@ struct FileError: Swift.Error, CustomStringConvertible, LocalizedError { var errorDescription: String? { description } + + static func notFoundFileErrors(forTarget targetName: String) -> [FileError] { + FileError.Kind.allCases.map { kind in + FileError(targetName: targetName, fileKind: kind, issue: .noFilesFound) + } + } +} + + +private extension [FileError] { + /// The error is definitely due to misconfiguration of a target. + var isDefiniteMisconfigurationError: Bool { + if count == FileError.Kind.allCases.count, + self.map(\.issue.isDefiniteMisconfigurationError).allSatisfy({ $0 == false }) { + return false + } + return true + } } From cb4349d173cd34cc50e2c6020e566a57c7a72146 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Tue, 18 Jul 2023 21:35:02 +0330 Subject: [PATCH 072/100] fixes --- Plugins/OpenAPIGeneratorCommand/plugin.swift | 4 ++-- Plugins/PluginsShared/PluginError.swift | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Plugins/OpenAPIGeneratorCommand/plugin.swift b/Plugins/OpenAPIGeneratorCommand/plugin.swift index e8ab04a1..ca97f0fe 100644 --- a/Plugins/OpenAPIGeneratorCommand/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommand/plugin.swift @@ -85,8 +85,9 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { throw error } } + } - guard hadASuccessfulRun else { + guard hadASuccessfulRun else { // None of the targets had a successful run and none had any definite // indication of trying to be OpenAPI-generator compatible. // To follow how the build command works, we throw file errors to let users @@ -96,7 +97,6 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { targetName: mainTarget.name ) } - } default: throw PluginError.tooManyTargetsMatchingTargetName( targetName: targetName, diff --git a/Plugins/PluginsShared/PluginError.swift b/Plugins/PluginsShared/PluginError.swift index 3b74f65c..8ddd9e4f 100644 --- a/Plugins/PluginsShared/PluginError.swift +++ b/Plugins/PluginsShared/PluginError.swift @@ -108,8 +108,10 @@ struct FileError: Swift.Error, CustomStringConvertible, LocalizedError { private extension [FileError] { /// The error is definitely due to misconfiguration of a target. var isDefiniteMisconfigurationError: Bool { + // If errors for both files exist and none is "Definite Misconfiguration Error" then the error + // can be related to a target that isn't supposed to be generator compatible at all. if count == FileError.Kind.allCases.count, - self.map(\.issue.isDefiniteMisconfigurationError).allSatisfy({ $0 == false }) { + self.allSatisfy({ !$0.issue.isDefiniteMisconfigurationError }) { return false } return true From cc26a9437baba44732bd5dd2c9383e0ae3e1c5b5 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Wed, 19 Jul 2023 10:02:07 +0330 Subject: [PATCH 073/100] more refinements --- Plugins/OpenAPIGeneratorCommand/plugin.swift | 13 ++++------ Plugins/PluginsShared/PluginError.swift | 25 +++++++++++--------- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/Plugins/OpenAPIGeneratorCommand/plugin.swift b/Plugins/OpenAPIGeneratorCommand/plugin.swift index ca97f0fe..df30326a 100644 --- a/Plugins/OpenAPIGeneratorCommand/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommand/plugin.swift @@ -88,15 +88,10 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { } guard hadASuccessfulRun else { - // None of the targets had a successful run and none had any definite - // indication of trying to be OpenAPI-generator compatible. - // To follow how the build command works, we throw file errors to let users - // know they need to add these files to their target. - throw PluginError.fileErrors( - FileError.notFoundFileErrors(forTarget: mainTarget.name), - targetName: mainTarget.name - ) - } + throw PluginError.noTargetOrDependenciesWithExpectedFiles( + targetName: mainTarget.name + ) + } default: throw PluginError.tooManyTargetsMatchingTargetName( targetName: targetName, diff --git a/Plugins/PluginsShared/PluginError.swift b/Plugins/PluginsShared/PluginError.swift index 8ddd9e4f..9ea632d3 100644 --- a/Plugins/PluginsShared/PluginError.swift +++ b/Plugins/PluginsShared/PluginError.swift @@ -3,27 +3,20 @@ import Foundation enum PluginError: Swift.Error, CustomStringConvertible, LocalizedError { case incompatibleTarget(targetName: String) + case noTargetOrDependenciesWithExpectedFiles(targetName: String) case badArguments([String]) case noTargetsMatchingTargetName(targetName: String) case tooManyTargetsMatchingTargetName(targetName: String, matchingTargetNames: [String]) case fileErrors([FileError], targetName: String) - /// The error is definitely due to misconfiguration of a target. - var isDefiniteMisconfigurationError: Bool { - switch self { - case .incompatibleTarget, .badArguments, .noTargetsMatchingTargetName, .tooManyTargetsMatchingTargetName: - return false - case .fileErrors(let errors, _): - return errors.isDefiniteMisconfigurationError - } - } - var description: String { switch self { case .incompatibleTarget(let targetName): return "Incompatible target called '\(targetName)'. Only Swift source targets can be used with the Swift OpenAPI Generator plugin." + case .noTargetOrDependenciesWithExpectedFiles(let targetName): + return "Target called '\(targetName)' and its dependencies don't contain any config or document files with expected names. For OpenAPI code generation, a target needs to contain a config file named 'openapi-generator-config.yaml' or 'openapi-generator-config.yml', as well as an OpenAPI document named 'openapi.yaml', 'openapi.yml' or 'openapi.json' under target's source directory. See documentation for details." case .badArguments(let arguments): - return "Unexpected arguments: \(arguments). On Xcode, use Xcode's command plugin UI to choose one specific target before hitting 'Run'. Otherwise make sure arguments are exactly of form '--target '." + return "Unexpected arguments: \(arguments). On Xcode, use Xcode's command plugin UI to choose one specific target before hitting 'Run'. On CLI make sure arguments are exactly of form '--target '." case .noTargetsMatchingTargetName(let targetName): return "Found no targets matching target name '\(targetName)'. Please make sure the target name argument leads to one and only one target." case .tooManyTargetsMatchingTargetName(let targetName, let matchingTargetNames): @@ -36,6 +29,16 @@ enum PluginError: Swift.Error, CustomStringConvertible, LocalizedError { var errorDescription: String? { description } + + /// The error is definitely due to misconfiguration of a target. + var isDefiniteMisconfigurationError: Bool { + switch self { + case .incompatibleTarget, .noTargetOrDependenciesWithExpectedFiles, .badArguments, .noTargetsMatchingTargetName, .tooManyTargetsMatchingTargetName: + return false + case .fileErrors(let errors, _): + return errors.isDefiniteMisconfigurationError + } + } } extension [PluginError]: Swift.Error, CustomStringConvertible, LocalizedError { From 0bc0d6cd21e646c4d2424d550268e15c10152342 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Wed, 19 Jul 2023 10:12:59 +0330 Subject: [PATCH 074/100] more on-point errors --- Plugins/PluginsShared/PluginError.swift | 16 +++++----------- Plugins/PluginsShared/PluginUtils.swift | 6 +++--- .../GenerateCommand.swift | 2 +- 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/Plugins/PluginsShared/PluginError.swift b/Plugins/PluginsShared/PluginError.swift index 9ea632d3..d5cae529 100644 --- a/Plugins/PluginsShared/PluginError.swift +++ b/Plugins/PluginsShared/PluginError.swift @@ -7,7 +7,7 @@ enum PluginError: Swift.Error, CustomStringConvertible, LocalizedError { case badArguments([String]) case noTargetsMatchingTargetName(targetName: String) case tooManyTargetsMatchingTargetName(targetName: String, matchingTargetNames: [String]) - case fileErrors([FileError], targetName: String) + case fileErrors([FileError]) var description: String { switch self { @@ -16,13 +16,13 @@ enum PluginError: Swift.Error, CustomStringConvertible, LocalizedError { case .noTargetOrDependenciesWithExpectedFiles(let targetName): return "Target called '\(targetName)' and its dependencies don't contain any config or document files with expected names. For OpenAPI code generation, a target needs to contain a config file named 'openapi-generator-config.yaml' or 'openapi-generator-config.yml', as well as an OpenAPI document named 'openapi.yaml', 'openapi.yml' or 'openapi.json' under target's source directory. See documentation for details." case .badArguments(let arguments): - return "Unexpected arguments: \(arguments). On Xcode, use Xcode's command plugin UI to choose one specific target before hitting 'Run'. On CLI make sure arguments are exactly of form '--target '." + return "On Xcode, use Xcode's command plugin UI to choose one specific target before hitting 'Run'. On CLI make sure arguments are exactly of form '--target '. The reason for this error is unexpected arguments: \(arguments)" case .noTargetsMatchingTargetName(let targetName): return "Found no targets matching target name '\(targetName)'. Please make sure the target name argument leads to one and only one target." case .tooManyTargetsMatchingTargetName(let targetName, let matchingTargetNames): return "Found too many targets matching target name '\(targetName)': \(matchingTargetNames). Please make sure the target name argument leads to a unique target." - case .fileErrors(let errors, let targetName): - return "Found file errors in target called '\(targetName)': \(errors)." + case .fileErrors(let errors): + return "Found file errors: \(errors)." } } @@ -35,18 +35,12 @@ enum PluginError: Swift.Error, CustomStringConvertible, LocalizedError { switch self { case .incompatibleTarget, .noTargetOrDependenciesWithExpectedFiles, .badArguments, .noTargetsMatchingTargetName, .tooManyTargetsMatchingTargetName: return false - case .fileErrors(let errors, _): + case .fileErrors(let errors): return errors.isDefiniteMisconfigurationError } } } -extension [PluginError]: Swift.Error, CustomStringConvertible, LocalizedError { - public var errorDescription: String? { - description - } -} - struct FileError: Swift.Error, CustomStringConvertible, LocalizedError { enum Kind: CaseIterable { diff --git a/Plugins/PluginsShared/PluginUtils.swift b/Plugins/PluginsShared/PluginUtils.swift index b9022e78..38b751c3 100644 --- a/Plugins/PluginsShared/PluginUtils.swift +++ b/Plugins/PluginsShared/PluginUtils.swift @@ -52,11 +52,11 @@ enum PluginUtils { let doc = findDocument(inputFiles: inputFiles, targetName: targetName) switch (config, doc) { case (.failure(let error1), .failure(let error2)): - throw PluginError.fileErrors([error1, error2], targetName: targetName) + throw PluginError.fileErrors([error1, error2]) case (_, .failure(let error)): - throw PluginError.fileErrors([error], targetName: targetName) + throw PluginError.fileErrors([error]) case (.failure(let error), _): - throw PluginError.fileErrors([error], targetName: targetName) + throw PluginError.fileErrors([error]) case (.success(let config), .success(let doc)): return (config, doc) } diff --git a/Sources/swift-openapi-generator/GenerateCommand.swift b/Sources/swift-openapi-generator/GenerateCommand.swift index 43845412..6ee3011f 100644 --- a/Sources/swift-openapi-generator/GenerateCommand.swift +++ b/Sources/swift-openapi-generator/GenerateCommand.swift @@ -44,7 +44,7 @@ struct _GenerateCommand: AsyncParsableCommand { @Option( help: - "Whether this invocation is from the SwiftPM plugin. We always need to produce all files when invoked from the plugin. Non-requested modes produce empty files." + "Source of invocation if by a plugin. The generator needs to produce all files when invoked as a build plugin, so non-requested modes produce empty files." ) var invokedFrom: PluginSource? From a97bc8ed6bacc14c22cea968ca7942622f398e13 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Wed, 19 Jul 2023 10:21:40 +0330 Subject: [PATCH 075/100] better errors --- Plugins/OpenAPIGeneratorCommand/plugin.swift | 3 ++- Plugins/PluginsShared/PluginError.swift | 9 ++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Plugins/OpenAPIGeneratorCommand/plugin.swift b/Plugins/OpenAPIGeneratorCommand/plugin.swift index df30326a..1db28247 100644 --- a/Plugins/OpenAPIGeneratorCommand/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommand/plugin.swift @@ -89,7 +89,8 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { guard hadASuccessfulRun else { throw PluginError.noTargetOrDependenciesWithExpectedFiles( - targetName: mainTarget.name + targetName: mainTarget.name, + dependencyNames: dependenciesInPackage.map(\.name) ) } default: diff --git a/Plugins/PluginsShared/PluginError.swift b/Plugins/PluginsShared/PluginError.swift index d5cae529..c0aa5a8a 100644 --- a/Plugins/PluginsShared/PluginError.swift +++ b/Plugins/PluginsShared/PluginError.swift @@ -3,7 +3,7 @@ import Foundation enum PluginError: Swift.Error, CustomStringConvertible, LocalizedError { case incompatibleTarget(targetName: String) - case noTargetOrDependenciesWithExpectedFiles(targetName: String) + case noTargetOrDependenciesWithExpectedFiles(targetName: String, dependencyNames: [String]) case badArguments([String]) case noTargetsMatchingTargetName(targetName: String) case tooManyTargetsMatchingTargetName(targetName: String, matchingTargetNames: [String]) @@ -13,8 +13,11 @@ enum PluginError: Swift.Error, CustomStringConvertible, LocalizedError { switch self { case .incompatibleTarget(let targetName): return "Incompatible target called '\(targetName)'. Only Swift source targets can be used with the Swift OpenAPI Generator plugin." - case .noTargetOrDependenciesWithExpectedFiles(let targetName): - return "Target called '\(targetName)' and its dependencies don't contain any config or document files with expected names. For OpenAPI code generation, a target needs to contain a config file named 'openapi-generator-config.yaml' or 'openapi-generator-config.yml', as well as an OpenAPI document named 'openapi.yaml', 'openapi.yml' or 'openapi.json' under target's source directory. See documentation for details." + case .noTargetOrDependenciesWithExpectedFiles(let targetName, let dependencyNames): + let introduction = dependencyNames.isEmpty ? + "Target called '\(targetName)' doesn't contain" : + "Target called '\(targetName)' and its dependencies \(dependencyNames) don't contain" + return "\(introduction) any config or document files with expected names. For OpenAPI code generation, a target needs to contain a config file named 'openapi-generator-config.yaml' or 'openapi-generator-config.yml', as well as an OpenAPI document named 'openapi.yaml', 'openapi.yml' or 'openapi.json' under target's source directory. See documentation for details." case .badArguments(let arguments): return "On Xcode, use Xcode's command plugin UI to choose one specific target before hitting 'Run'. On CLI make sure arguments are exactly of form '--target '. The reason for this error is unexpected arguments: \(arguments)" case .noTargetsMatchingTargetName(let targetName): From 11ee7667bd10972073224db7419602854b284f0a Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Wed, 19 Jul 2023 10:26:23 +0330 Subject: [PATCH 076/100] more minor refinements --- Plugins/PluginsShared/PluginError.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Plugins/PluginsShared/PluginError.swift b/Plugins/PluginsShared/PluginError.swift index c0aa5a8a..15594b92 100644 --- a/Plugins/PluginsShared/PluginError.swift +++ b/Plugins/PluginsShared/PluginError.swift @@ -16,7 +16,7 @@ enum PluginError: Swift.Error, CustomStringConvertible, LocalizedError { case .noTargetOrDependenciesWithExpectedFiles(let targetName, let dependencyNames): let introduction = dependencyNames.isEmpty ? "Target called '\(targetName)' doesn't contain" : - "Target called '\(targetName)' and its dependencies \(dependencyNames) don't contain" + "Target called '\(targetName)' or its dependencies \(dependencyNames) don't contain" return "\(introduction) any config or document files with expected names. For OpenAPI code generation, a target needs to contain a config file named 'openapi-generator-config.yaml' or 'openapi-generator-config.yml', as well as an OpenAPI document named 'openapi.yaml', 'openapi.yml' or 'openapi.json' under target's source directory. See documentation for details." case .badArguments(let arguments): return "On Xcode, use Xcode's command plugin UI to choose one specific target before hitting 'Run'. On CLI make sure arguments are exactly of form '--target '. The reason for this error is unexpected arguments: \(arguments)" @@ -108,8 +108,8 @@ struct FileError: Swift.Error, CustomStringConvertible, LocalizedError { private extension [FileError] { /// The error is definitely due to misconfiguration of a target. var isDefiniteMisconfigurationError: Bool { - // If errors for both files exist and none is "Definite Misconfiguration Error" then the error - // can be related to a target that isn't supposed to be generator compatible at all. + // If errors for both files exist and none is "Definite Misconfiguration Error" then the + // error can be related to a target that isn't supposed to be generator compatible at all. if count == FileError.Kind.allCases.count, self.allSatisfy({ !$0.issue.isDefiniteMisconfigurationError }) { return false From 75b534ec5277160efe582ed434ffda89cb21865d Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Wed, 19 Jul 2023 10:29:54 +0330 Subject: [PATCH 077/100] soundness? --- Plugins/PluginsShared/PluginError.swift | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Plugins/PluginsShared/PluginError.swift b/Plugins/PluginsShared/PluginError.swift index 15594b92..11c029f1 100644 --- a/Plugins/PluginsShared/PluginError.swift +++ b/Plugins/PluginsShared/PluginError.swift @@ -14,9 +14,7 @@ enum PluginError: Swift.Error, CustomStringConvertible, LocalizedError { case .incompatibleTarget(let targetName): return "Incompatible target called '\(targetName)'. Only Swift source targets can be used with the Swift OpenAPI Generator plugin." case .noTargetOrDependenciesWithExpectedFiles(let targetName, let dependencyNames): - let introduction = dependencyNames.isEmpty ? - "Target called '\(targetName)' doesn't contain" : - "Target called '\(targetName)' or its dependencies \(dependencyNames) don't contain" + let introduction = dependencyNames.isEmpty ? "Target called '\(targetName)' doesn't contain" : "Target called '\(targetName)' or its dependencies \(dependencyNames) don't contain" return "\(introduction) any config or document files with expected names. For OpenAPI code generation, a target needs to contain a config file named 'openapi-generator-config.yaml' or 'openapi-generator-config.yml', as well as an OpenAPI document named 'openapi.yaml', 'openapi.yml' or 'openapi.json' under target's source directory. See documentation for details." case .badArguments(let arguments): return "On Xcode, use Xcode's command plugin UI to choose one specific target before hitting 'Run'. On CLI make sure arguments are exactly of form '--target '. The reason for this error is unexpected arguments: \(arguments)" @@ -111,7 +109,8 @@ private extension [FileError] { // If errors for both files exist and none is "Definite Misconfiguration Error" then the // error can be related to a target that isn't supposed to be generator compatible at all. if count == FileError.Kind.allCases.count, - self.allSatisfy({ !$0.issue.isDefiniteMisconfigurationError }) { + self.allSatisfy({ !$0.issue.isDefiniteMisconfigurationError }) + { return false } return true From fefdde36955d63dd78db513b726f6570ad437347 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Wed, 19 Jul 2023 10:33:03 +0330 Subject: [PATCH 078/100] more soundness --- Plugins/PluginsShared/PluginError.swift | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Plugins/PluginsShared/PluginError.swift b/Plugins/PluginsShared/PluginError.swift index 11c029f1..b60fde6d 100644 --- a/Plugins/PluginsShared/PluginError.swift +++ b/Plugins/PluginsShared/PluginError.swift @@ -34,7 +34,15 @@ enum PluginError: Swift.Error, CustomStringConvertible, LocalizedError { /// The error is definitely due to misconfiguration of a target. var isDefiniteMisconfigurationError: Bool { switch self { - case .incompatibleTarget, .noTargetOrDependenciesWithExpectedFiles, .badArguments, .noTargetsMatchingTargetName, .tooManyTargetsMatchingTargetName: + case .incompatibleTarget: + return false + case .noTargetOrDependenciesWithExpectedFiles: + return false + case .badArguments: + return false + case .noTargetsMatchingTargetName: + return false + case .tooManyTargetsMatchingTargetName: return false case .fileErrors(let errors): return errors.isDefiniteMisconfigurationError @@ -102,7 +110,6 @@ struct FileError: Swift.Error, CustomStringConvertible, LocalizedError { } } - private extension [FileError] { /// The error is definitely due to misconfiguration of a target. var isDefiniteMisconfigurationError: Bool { From 80521cec3574f47b2d32f3b8038ac5f7b0fe4014 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Wed, 19 Jul 2023 10:38:59 +0330 Subject: [PATCH 079/100] minor refinements --- Plugins/OpenAPIGeneratorCommand/plugin.swift | 6 +++--- Plugins/PluginsShared/PluginError.swift | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Plugins/OpenAPIGeneratorCommand/plugin.swift b/Plugins/OpenAPIGeneratorCommand/plugin.swift index 1db28247..8446bbbd 100644 --- a/Plugins/OpenAPIGeneratorCommand/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommand/plugin.swift @@ -64,11 +64,11 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { } let allDependencies = mainTarget.recursiveTargetDependencies let packageTargets = Set(context.package.targets.map(\.id)) - let dependenciesInPackage = allDependencies.filter { packageTargets.contains($0.id) } + let localDependencies = allDependencies.filter { packageTargets.contains($0.id) } var hadASuccessfulRun = false - for target in [mainTarget] + dependenciesInPackage { + for target in [mainTarget] + localDependencies { guard let swiftTarget = target as? SwiftSourceModuleTarget else { continue } @@ -90,7 +90,7 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { guard hadASuccessfulRun else { throw PluginError.noTargetOrDependenciesWithExpectedFiles( targetName: mainTarget.name, - dependencyNames: dependenciesInPackage.map(\.name) + dependencyNames: localDependencies.map(\.name) ) } default: diff --git a/Plugins/PluginsShared/PluginError.swift b/Plugins/PluginsShared/PluginError.swift index b60fde6d..3bf8a4dd 100644 --- a/Plugins/PluginsShared/PluginError.swift +++ b/Plugins/PluginsShared/PluginError.swift @@ -14,7 +14,7 @@ enum PluginError: Swift.Error, CustomStringConvertible, LocalizedError { case .incompatibleTarget(let targetName): return "Incompatible target called '\(targetName)'. Only Swift source targets can be used with the Swift OpenAPI Generator plugin." case .noTargetOrDependenciesWithExpectedFiles(let targetName, let dependencyNames): - let introduction = dependencyNames.isEmpty ? "Target called '\(targetName)' doesn't contain" : "Target called '\(targetName)' or its dependencies \(dependencyNames) don't contain" + let introduction = dependencyNames.isEmpty ? "Target called '\(targetName)' doesn't contain" : "Target called '\(targetName)' or its local dependencies \(dependencyNames) don't contain" return "\(introduction) any config or document files with expected names. For OpenAPI code generation, a target needs to contain a config file named 'openapi-generator-config.yaml' or 'openapi-generator-config.yml', as well as an OpenAPI document named 'openapi.yaml', 'openapi.yml' or 'openapi.json' under target's source directory. See documentation for details." case .badArguments(let arguments): return "On Xcode, use Xcode's command plugin UI to choose one specific target before hitting 'Run'. On CLI make sure arguments are exactly of form '--target '. The reason for this error is unexpected arguments: \(arguments)" From dd7013883c67d99f83610e58a4ecada9eae32a4f Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Wed, 19 Jul 2023 12:14:34 +0330 Subject: [PATCH 080/100] Update Package.swift Co-authored-by: Honza Dvorsky --- Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index 45cd4afc..c8dc4225 100644 --- a/Package.swift +++ b/Package.swift @@ -156,7 +156,7 @@ let package = Package( // Command Plugin .plugin( name: "OpenAPIGeneratorCommand", - capability: Target.PluginCapability.command( + capability: .command( intent: .custom( verb: "generate-openapi-code", description: "Generates Swift code from an OpenAPI document." From 74efe1d830f39a0a026df38bea5424f953a67607 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Wed, 19 Jul 2023 12:21:20 +0330 Subject: [PATCH 081/100] Update Package.swift Co-authored-by: Honza Dvorsky --- Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index c8dc4225..d567b16b 100644 --- a/Package.swift +++ b/Package.swift @@ -159,7 +159,7 @@ let package = Package( capability: .command( intent: .custom( verb: "generate-openapi-code", - description: "Generates Swift code from an OpenAPI document." + description: "Generate Swift code from an OpenAPI document." ), permissions: [.writeToPackageDirectory(reason: "To write the generated Swift files back into the source directory of the package.")] ), From 1cffe16657294c826954d60c2a9ec68f36db55a9 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Wed, 19 Jul 2023 12:44:31 +0330 Subject: [PATCH 082/100] Update Package.swift Co-authored-by: Honza Dvorsky --- Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index d567b16b..3513259f 100644 --- a/Package.swift +++ b/Package.swift @@ -158,7 +158,7 @@ let package = Package( name: "OpenAPIGeneratorCommand", capability: .command( intent: .custom( - verb: "generate-openapi-code", + verb: "generate-code-from-openapi", description: "Generate Swift code from an OpenAPI document." ), permissions: [.writeToPackageDirectory(reason: "To write the generated Swift files back into the source directory of the package.")] From 39626523eb55e1ccaa1347a65bc02c5af07a471a Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Wed, 19 Jul 2023 18:58:54 +0330 Subject: [PATCH 083/100] partially apply requested changes --- Plugins/OpenAPIGenerator/plugin.swift | 2 +- Plugins/OpenAPIGeneratorCommand/plugin.swift | 11 +-- Plugins/PluginsShared/PluginError.swift | 70 +++++++++---------- Plugins/PluginsShared/PluginUtils.swift | 2 +- .../GenerateCommand.swift | 4 +- .../GenerateOptions+runGenerator.swift | 2 +- .../PluginSource.swift | 2 + .../runGenerator.swift | 10 +-- 8 files changed, 52 insertions(+), 51 deletions(-) diff --git a/Plugins/OpenAPIGenerator/plugin.swift b/Plugins/OpenAPIGenerator/plugin.swift index 98744ef1..d904d7c7 100644 --- a/Plugins/OpenAPIGenerator/plugin.swift +++ b/Plugins/OpenAPIGenerator/plugin.swift @@ -53,7 +53,7 @@ extension SwiftOpenAPIGeneratorPlugin: BuildToolPlugin { target: Target ) async throws -> [Command] { guard let swiftTarget = target as? SwiftSourceModuleTarget else { - throw PluginError.incompatibleTarget(targetName: target.name) + throw PluginError.incompatibleTarget(name: target.name) } return try createBuildCommands( pluginWorkDirectory: context.pluginWorkDirectory, diff --git a/Plugins/OpenAPIGeneratorCommand/plugin.swift b/Plugins/OpenAPIGeneratorCommand/plugin.swift index 8446bbbd..0a716a1b 100644 --- a/Plugins/OpenAPIGeneratorCommand/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommand/plugin.swift @@ -37,6 +37,9 @@ struct SwiftOpenAPIGeneratorPlugin { process.environment = [:] try process.run() process.waitUntilExit() + guard process.terminationStatus == 0 else { + throw PluginError.generatorFailure(targetName: targetName) + } } } @@ -56,11 +59,11 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { switch matchingTargets.count { case 0: - throw PluginError.noTargetsMatchingTargetName(targetName: targetName) + throw PluginError.noTargetsMatchingTargetName(targetName) case 1: let mainTarget = matchingTargets[0] guard mainTarget is SwiftSourceModuleTarget else { - throw PluginError.incompatibleTarget(targetName: mainTarget.name) + throw PluginError.incompatibleTarget(name: mainTarget.name) } let allDependencies = mainTarget.recursiveTargetDependencies let packageTargets = Set(context.package.targets.map(\.id)) @@ -81,7 +84,7 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { ) hadASuccessfulRun = true } catch let error as PluginError { - if error.isDefiniteMisconfigurationError { + if error.isMisconfigurationError { throw error } } @@ -95,7 +98,7 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { } default: throw PluginError.tooManyTargetsMatchingTargetName( - targetName: targetName, + targetName, matchingTargetNames: matchingTargets.map(\.name) ) } diff --git a/Plugins/PluginsShared/PluginError.swift b/Plugins/PluginsShared/PluginError.swift index 3bf8a4dd..5b13d81c 100644 --- a/Plugins/PluginsShared/PluginError.swift +++ b/Plugins/PluginsShared/PluginError.swift @@ -2,28 +2,31 @@ import PackagePlugin import Foundation enum PluginError: Swift.Error, CustomStringConvertible, LocalizedError { - case incompatibleTarget(targetName: String) + case incompatibleTarget(name: String) + case generatorFailure(targetName: String) case noTargetOrDependenciesWithExpectedFiles(targetName: String, dependencyNames: [String]) case badArguments([String]) - case noTargetsMatchingTargetName(targetName: String) - case tooManyTargetsMatchingTargetName(targetName: String, matchingTargetNames: [String]) + case noTargetsMatchingTargetName(String) + case tooManyTargetsMatchingTargetName(String, matchingTargetNames: [String]) case fileErrors([FileError]) var description: String { switch self { - case .incompatibleTarget(let targetName): - return "Incompatible target called '\(targetName)'. Only Swift source targets can be used with the Swift OpenAPI Generator plugin." + case .incompatibleTarget(let name): + return "Incompatible target called '\(name)'. Only Swift source targets can be used with the Swift OpenAPI Generator plugin." + case .generatorFailure(let targetName): + return "The generator failed to generate OpenAPI files for target '\(targetName)'." case .noTargetOrDependenciesWithExpectedFiles(let targetName, let dependencyNames): - let introduction = dependencyNames.isEmpty ? "Target called '\(targetName)' doesn't contain" : "Target called '\(targetName)' or its local dependencies \(dependencyNames) don't contain" - return "\(introduction) any config or document files with expected names. For OpenAPI code generation, a target needs to contain a config file named 'openapi-generator-config.yaml' or 'openapi-generator-config.yml', as well as an OpenAPI document named 'openapi.yaml', 'openapi.yml' or 'openapi.json' under target's source directory. See documentation for details." + let introduction = dependencyNames.isEmpty ? "Target called '\(targetName)' doesn't contain" : "Target called '\(targetName)' or its local dependencies \(dependencyNames.joined(separator: ", ")) don't contain" + return "\(introduction) any config or OpenAPI document files with expected names. See documentation for details." case .badArguments(let arguments): - return "On Xcode, use Xcode's command plugin UI to choose one specific target before hitting 'Run'. On CLI make sure arguments are exactly of form '--target '. The reason for this error is unexpected arguments: \(arguments)" + return "Unexpected arguments: '\(arguments.joined(separator: " "))', expected: '--target MyTarget'." case .noTargetsMatchingTargetName(let targetName): - return "Found no targets matching target name '\(targetName)'. Please make sure the target name argument leads to one and only one target." + return "Found no targets with the name '\(targetName)'." case .tooManyTargetsMatchingTargetName(let targetName, let matchingTargetNames): - return "Found too many targets matching target name '\(targetName)': \(matchingTargetNames). Please make sure the target name argument leads to a unique target." - case .fileErrors(let errors): - return "Found file errors: \(errors)." + return "Found too many targets with the name '\(targetName)': \(matchingTargetNames.joined(separator: ", ")). Select a target name with a unique name." + case .fileErrors(let fileErrors): + return "Issues with required files: \(fileErrors.map(\.description).joined(separator: ", and"))." } } @@ -32,37 +35,40 @@ enum PluginError: Swift.Error, CustomStringConvertible, LocalizedError { } /// The error is definitely due to misconfiguration of a target. - var isDefiniteMisconfigurationError: Bool { + var isMisconfigurationError: Bool { switch self { - case .incompatibleTarget: - return false - case .noTargetOrDependenciesWithExpectedFiles: - return false - case .badArguments: - return false - case .noTargetsMatchingTargetName: - return false - case .tooManyTargetsMatchingTargetName: + case .incompatibleTarget, + .generatorFailure, + .noTargetOrDependenciesWithExpectedFiles, + .badArguments, + .noTargetsMatchingTargetName, + .tooManyTargetsMatchingTargetName: return false case .fileErrors(let errors): - return errors.isDefiniteMisconfigurationError + return errors.isMisconfigurationError } } } struct FileError: Swift.Error, CustomStringConvertible, LocalizedError { + /// The kind of the file. enum Kind: CaseIterable { + /// Config file. case config + /// OpenAPI document file. case document } + /// Encountered issue. enum Issue { + /// File wasn't found. case noFilesFound + /// More than 1 file found. case multipleFilesFound(files: [Path]) /// The error is definitely due to misconfiguration of a target. - var isDefiniteMisconfigurationError: Bool { + var isMisconfigurationError: Bool { switch self { case .noFilesFound: return false @@ -77,10 +83,6 @@ struct FileError: Swift.Error, CustomStringConvertible, LocalizedError { let issue: Issue var description: String { - "FileError { \(helpAnchor!) }" - } - - var helpAnchor: String? { switch fileKind { case .config: switch issue { @@ -102,21 +104,15 @@ struct FileError: Swift.Error, CustomStringConvertible, LocalizedError { var errorDescription: String? { description } - - static func notFoundFileErrors(forTarget targetName: String) -> [FileError] { - FileError.Kind.allCases.map { kind in - FileError(targetName: targetName, fileKind: kind, issue: .noFilesFound) - } - } } -private extension [FileError] { +private extension Array where Element == FileError { /// The error is definitely due to misconfiguration of a target. - var isDefiniteMisconfigurationError: Bool { + var isMisconfigurationError: Bool { // If errors for both files exist and none is "Definite Misconfiguration Error" then the // error can be related to a target that isn't supposed to be generator compatible at all. if count == FileError.Kind.allCases.count, - self.allSatisfy({ !$0.issue.isDefiniteMisconfigurationError }) + self.allSatisfy({ !$0.issue.isMisconfigurationError }) { return false } diff --git a/Plugins/PluginsShared/PluginUtils.swift b/Plugins/PluginsShared/PluginUtils.swift index 38b751c3..092af7d7 100644 --- a/Plugins/PluginsShared/PluginUtils.swift +++ b/Plugins/PluginsShared/PluginUtils.swift @@ -28,7 +28,7 @@ enum PluginUtils { "generate", "\(doc)", "--config", "\(config)", "--output-directory", "\(genSourcesDir)", - "--invoked-from", "\(pluginSource.rawValue)", + "--plugin-source", "\(pluginSource.rawValue)", ] let tool = try tool("swift-openapi-generator") diff --git a/Sources/swift-openapi-generator/GenerateCommand.swift b/Sources/swift-openapi-generator/GenerateCommand.swift index 6ee3011f..06e90fab 100644 --- a/Sources/swift-openapi-generator/GenerateCommand.swift +++ b/Sources/swift-openapi-generator/GenerateCommand.swift @@ -46,12 +46,12 @@ struct _GenerateCommand: AsyncParsableCommand { help: "Source of invocation if by a plugin. The generator needs to produce all files when invoked as a build plugin, so non-requested modes produce empty files." ) - var invokedFrom: PluginSource? + var pluginSource: PluginSource? func run() async throws { try generate.runGenerator( outputDirectory: outputDirectory, - pluginSource: invokedFrom + pluginSource: pluginSource ) } } diff --git a/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift b/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift index 8d6571fa..5c272af0 100644 --- a/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift +++ b/Sources/swift-openapi-generator/GenerateOptions+runGenerator.swift @@ -57,7 +57,7 @@ extension _GenerateOptions { - Output directory: \(outputDirectory.path) - Diagnostics output path: \(diagnosticsOutputPath?.path ?? "") - Current directory: \(FileManager.default.currentDirectoryPath) - - Plugin invoked from: \(pluginSource?.rawValue ?? "") + - Plugin source: \(pluginSource?.rawValue ?? "") - Additional imports: \(resolvedAdditionalImports.isEmpty ? "" : resolvedAdditionalImports.joined(separator: ", ")) """ ) diff --git a/Sources/swift-openapi-generator/PluginSource.swift b/Sources/swift-openapi-generator/PluginSource.swift index b7fd8722..2afb05af 100644 --- a/Sources/swift-openapi-generator/PluginSource.swift +++ b/Sources/swift-openapi-generator/PluginSource.swift @@ -1,5 +1,7 @@ /// The source of a plugin generator invocation. enum PluginSource: String, Codable { + /// BuildTool plugin case build + /// Command plugin. case command } diff --git a/Sources/swift-openapi-generator/runGenerator.swift b/Sources/swift-openapi-generator/runGenerator.swift index 0ccd9a8f..53c58c89 100644 --- a/Sources/swift-openapi-generator/runGenerator.swift +++ b/Sources/swift-openapi-generator/runGenerator.swift @@ -110,11 +110,11 @@ extension _Tool { fileName: String, with contents: () throws -> Data ) throws -> Bool { - let fm = FileManager.default + let fileManager = FileManager.default // Create directory if it doesn't exist. - if !fm.fileExists(atPath: outputDirectory.path) { - try fm.createDirectory( + if !fileManager.fileExists(atPath: outputDirectory.path) { + try fileManager.createDirectory( at: outputDirectory, withIntermediateDirectories: true ) @@ -122,8 +122,8 @@ extension _Tool { let path = outputDirectory.appendingPathComponent(fileName) let data = try contents() - guard fm.fileExists(atPath: path.path) else { - return fm.createFile(atPath: path.path, contents: data) + guard fileManager.fileExists(atPath: path.path) else { + return fileManager.createFile(atPath: path.path, contents: data) } let existingData = try? Data(contentsOf: path) guard existingData == data else { From 26fefd50baf0b79d5a27f489f7b92efc88c24b64 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Thu, 20 Jul 2023 10:38:34 +0330 Subject: [PATCH 084/100] apply remaining requested changes --- Plugins/OpenAPIGeneratorCommand/plugin.swift | 90 ++++++++++---------- Plugins/PluginsShared/PluginError.swift | 47 +++++----- Plugins/PluginsShared/PluginUtils.swift | 10 +++ 3 files changed, 81 insertions(+), 66 deletions(-) diff --git a/Plugins/OpenAPIGeneratorCommand/plugin.swift b/Plugins/OpenAPIGeneratorCommand/plugin.swift index 0a716a1b..038d6576 100644 --- a/Plugins/OpenAPIGeneratorCommand/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommand/plugin.swift @@ -48,59 +48,57 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { context: PluginContext, arguments: [String] ) async throws { - guard arguments.count == 2, - arguments[0] == "--target" - else { - throw PluginError.badArguments(arguments) + let targetNameArguments = arguments.filter({ $0 != "--target" }) + let targets: [Target] + if targetNameArguments.isEmpty { + targets = context.package.targets + } else { + let matchingTargets = try context.package.targets(named: targetNameArguments) + let withDependencies = matchingTargets.flatMap { [$0] + $0.recursiveTargetDependencies } + let packageTargets = Set(context.package.targets.map(\.id)) + let withLocalDependencies = withDependencies.filter { packageTargets.contains($0.id) } + let enumeratedKeyValues = withLocalDependencies.map(\.id).enumerated().map { (key: $0.element, value: $0.offset) } + let indexLookupTable = Dictionary(enumeratedKeyValues, uniquingKeysWith: { l, _ in l }) + let groupedByID = Dictionary(grouping: withDependencies, by: \.id) + let sortedUniqueTargets = groupedByID.map(\.value[0]).sorted { + indexLookupTable[$0.id, default: 0] < indexLookupTable[$1.id, default: 0] + } + targets = sortedUniqueTargets } - let targetName = arguments[1] - - let matchingTargets = try context.package.targets(named: [targetName]) - switch matchingTargets.count { - case 0: - throw PluginError.noTargetsMatchingTargetName(targetName) - case 1: - let mainTarget = matchingTargets[0] - guard mainTarget is SwiftSourceModuleTarget else { - throw PluginError.incompatibleTarget(name: mainTarget.name) - } - let allDependencies = mainTarget.recursiveTargetDependencies - let packageTargets = Set(context.package.targets.map(\.id)) - let localDependencies = allDependencies.filter { packageTargets.contains($0.id) } + guard !targets.isEmpty else { + throw PluginError.noTargetsMatchingTargetNames(targetNameArguments) + } - var hadASuccessfulRun = false + var hadASuccessfulRun = false - for target in [mainTarget] + localDependencies { - guard let swiftTarget = target as? SwiftSourceModuleTarget else { - continue - } - do { - try runCommand( - targetWorkingDirectory: target.directory, - tool: context.tool, - sourceFiles: swiftTarget.sourceFiles, - targetName: target.name - ) - hadASuccessfulRun = true - } catch let error as PluginError { - if error.isMisconfigurationError { - throw error - } - } + for target in targets { + guard let swiftTarget = target as? SwiftSourceModuleTarget else { + print("- Target '\(target.name)': Not a Swift source module. Can't generate OpenAPI code.") + continue } - - guard hadASuccessfulRun else { - throw PluginError.noTargetOrDependenciesWithExpectedFiles( - targetName: mainTarget.name, - dependencyNames: localDependencies.map(\.name) + do { + try runCommand( + targetWorkingDirectory: target.directory, + tool: context.tool, + sourceFiles: swiftTarget.sourceFiles, + targetName: target.name ) + print("- Target '\(target.name)': OpenAPI code generation completed successfully.") + hadASuccessfulRun = true + } catch let error as PluginError { + if error.isMisconfigurationError { + throw error + } else { + let fileNames = FileError.Kind.allCases.map(\.name) + .joined(separator: ", ", lastSeparator: " or ") + print("- Target '\(target.name)': OpenAPI code generation failed. No expected \(fileNames) files.") + } } - default: - throw PluginError.tooManyTargetsMatchingTargetName( - targetName, - matchingTargetNames: matchingTargets.map(\.name) - ) + } + + guard hadASuccessfulRun else { + throw PluginError.noTargetsWithExpectedFiles(targetNames: targets.map(\.name)) } } } diff --git a/Plugins/PluginsShared/PluginError.swift b/Plugins/PluginsShared/PluginError.swift index 5b13d81c..31210f0c 100644 --- a/Plugins/PluginsShared/PluginError.swift +++ b/Plugins/PluginsShared/PluginError.swift @@ -4,10 +4,8 @@ import Foundation enum PluginError: Swift.Error, CustomStringConvertible, LocalizedError { case incompatibleTarget(name: String) case generatorFailure(targetName: String) - case noTargetOrDependenciesWithExpectedFiles(targetName: String, dependencyNames: [String]) - case badArguments([String]) - case noTargetsMatchingTargetName(String) - case tooManyTargetsMatchingTargetName(String, matchingTargetNames: [String]) + case noTargetsWithExpectedFiles(targetNames: [String]) + case noTargetsMatchingTargetNames([String]) case fileErrors([FileError]) var description: String { @@ -16,15 +14,14 @@ enum PluginError: Swift.Error, CustomStringConvertible, LocalizedError { return "Incompatible target called '\(name)'. Only Swift source targets can be used with the Swift OpenAPI Generator plugin." case .generatorFailure(let targetName): return "The generator failed to generate OpenAPI files for target '\(targetName)'." - case .noTargetOrDependenciesWithExpectedFiles(let targetName, let dependencyNames): - let introduction = dependencyNames.isEmpty ? "Target called '\(targetName)' doesn't contain" : "Target called '\(targetName)' or its local dependencies \(dependencyNames.joined(separator: ", ")) don't contain" - return "\(introduction) any config or OpenAPI document files with expected names. See documentation for details." - case .badArguments(let arguments): - return "Unexpected arguments: '\(arguments.joined(separator: " "))', expected: '--target MyTarget'." - case .noTargetsMatchingTargetName(let targetName): - return "Found no targets with the name '\(targetName)'." - case .tooManyTargetsMatchingTargetName(let targetName, let matchingTargetNames): - return "Found too many targets with the name '\(targetName)': \(matchingTargetNames.joined(separator: ", ")). Select a target name with a unique name." + case .noTargetsWithExpectedFiles(let targetNames): + let fileNames = FileError.Kind.allCases.map(\.name) + .joined(separator: ", ", lastSeparator: " or ") + let targetNames = targetNames.joined(separator: ", ", lastSeparator: " and ") + return "Targets with names '\(targetNames)' don't contain any \(fileNames) files with expected names. See documentation for details." + case .noTargetsMatchingTargetNames(let targetNames): + let targetNames = targetNames.joined(separator: ", ", lastSeparator: " and ") + return "Found no targets with names \(targetNames)." case .fileErrors(let fileErrors): return "Issues with required files: \(fileErrors.map(\.description).joined(separator: ", and"))." } @@ -37,12 +34,13 @@ enum PluginError: Swift.Error, CustomStringConvertible, LocalizedError { /// The error is definitely due to misconfiguration of a target. var isMisconfigurationError: Bool { switch self { - case .incompatibleTarget, - .generatorFailure, - .noTargetOrDependenciesWithExpectedFiles, - .badArguments, - .noTargetsMatchingTargetName, - .tooManyTargetsMatchingTargetName: + case .incompatibleTarget: + return false + case .generatorFailure: + return false + case .noTargetsWithExpectedFiles: + return false + case .noTargetsMatchingTargetNames: return false case .fileErrors(let errors): return errors.isMisconfigurationError @@ -58,6 +56,15 @@ struct FileError: Swift.Error, CustomStringConvertible, LocalizedError { case config /// OpenAPI document file. case document + + var name: String { + switch self { + case .config: + return "config" + case .document: + return "OpenAPI document" + } + } } /// Encountered issue. @@ -109,7 +116,7 @@ struct FileError: Swift.Error, CustomStringConvertible, LocalizedError { private extension Array where Element == FileError { /// The error is definitely due to misconfiguration of a target. var isMisconfigurationError: Bool { - // If errors for both files exist and none is "Definite Misconfiguration Error" then the + // If errors for both files exist and none is a "Misconfiguration Error" then the // error can be related to a target that isn't supposed to be generator compatible at all. if count == FileError.Kind.allCases.count, self.allSatisfy({ !$0.issue.isMisconfigurationError }) diff --git a/Plugins/PluginsShared/PluginUtils.swift b/Plugins/PluginsShared/PluginUtils.swift index 092af7d7..4ad22ce4 100644 --- a/Plugins/PluginsShared/PluginUtils.swift +++ b/Plugins/PluginsShared/PluginUtils.swift @@ -116,3 +116,13 @@ enum PluginUtils { return .success(matchedDocs[0]) } } + +extension Array where Element == String { + func joined(separator: String, lastSeparator: String) -> String { + if count > 1 { + return "\(self.dropLast().joined(separator: separator))\(lastSeparator)\(self.last!)" + } else { + return self.joined(separator: separator) + } + } +} From aa7a8a73beb5dd72e6c9d6a0ec68378eca0ee2c4 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Thu, 20 Jul 2023 10:42:50 +0330 Subject: [PATCH 085/100] fix wrong variable --- Plugins/OpenAPIGeneratorCommand/plugin.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/OpenAPIGeneratorCommand/plugin.swift b/Plugins/OpenAPIGeneratorCommand/plugin.swift index 038d6576..e1f9f22e 100644 --- a/Plugins/OpenAPIGeneratorCommand/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommand/plugin.swift @@ -59,7 +59,7 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { let withLocalDependencies = withDependencies.filter { packageTargets.contains($0.id) } let enumeratedKeyValues = withLocalDependencies.map(\.id).enumerated().map { (key: $0.element, value: $0.offset) } let indexLookupTable = Dictionary(enumeratedKeyValues, uniquingKeysWith: { l, _ in l }) - let groupedByID = Dictionary(grouping: withDependencies, by: \.id) + let groupedByID = Dictionary(grouping: withLocalDependencies, by: \.id) let sortedUniqueTargets = groupedByID.map(\.value[0]).sorted { indexLookupTable[$0.id, default: 0] < indexLookupTable[$1.id, default: 0] } From 653dc4a2d7ca4247d24883e509f3909be9ed7b43 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Thu, 20 Jul 2023 10:45:18 +0330 Subject: [PATCH 086/100] minor improvement --- Plugins/OpenAPIGeneratorCommand/plugin.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/OpenAPIGeneratorCommand/plugin.swift b/Plugins/OpenAPIGeneratorCommand/plugin.swift index e1f9f22e..954a083d 100644 --- a/Plugins/OpenAPIGeneratorCommand/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommand/plugin.swift @@ -84,7 +84,7 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { sourceFiles: swiftTarget.sourceFiles, targetName: target.name ) - print("- Target '\(target.name)': OpenAPI code generation completed successfully.") + print("- Target '\(target.name)': OpenAPI code generation successfully completed.") hadASuccessfulRun = true } catch let error as PluginError { if error.isMisconfigurationError { From b903f54fc6f212a0e904fa6153c41c6c99029a8b Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Thu, 20 Jul 2023 10:48:41 +0330 Subject: [PATCH 087/100] minor refinement --- Plugins/PluginsShared/PluginError.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/PluginsShared/PluginError.swift b/Plugins/PluginsShared/PluginError.swift index 31210f0c..005bb39a 100644 --- a/Plugins/PluginsShared/PluginError.swift +++ b/Plugins/PluginsShared/PluginError.swift @@ -18,7 +18,7 @@ enum PluginError: Swift.Error, CustomStringConvertible, LocalizedError { let fileNames = FileError.Kind.allCases.map(\.name) .joined(separator: ", ", lastSeparator: " or ") let targetNames = targetNames.joined(separator: ", ", lastSeparator: " and ") - return "Targets with names '\(targetNames)' don't contain any \(fileNames) files with expected names. See documentation for details." + return "Targets with names \(targetNames) don't contain any \(fileNames) files with expected names. See documentation for details." case .noTargetsMatchingTargetNames(let targetNames): let targetNames = targetNames.joined(separator: ", ", lastSeparator: " and ") return "Found no targets with names \(targetNames)." From 0e7b1ee72b5cb6e55aa07eca2e2bbb6b47d2f759 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Thu, 20 Jul 2023 10:52:41 +0330 Subject: [PATCH 088/100] soundness? --- Plugins/OpenAPIGeneratorCommand/plugin.swift | 4 +--- Plugins/PluginsShared/PluginError.swift | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/Plugins/OpenAPIGeneratorCommand/plugin.swift b/Plugins/OpenAPIGeneratorCommand/plugin.swift index 954a083d..9cd67756 100644 --- a/Plugins/OpenAPIGeneratorCommand/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommand/plugin.swift @@ -60,9 +60,7 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { let enumeratedKeyValues = withLocalDependencies.map(\.id).enumerated().map { (key: $0.element, value: $0.offset) } let indexLookupTable = Dictionary(enumeratedKeyValues, uniquingKeysWith: { l, _ in l }) let groupedByID = Dictionary(grouping: withLocalDependencies, by: \.id) - let sortedUniqueTargets = groupedByID.map(\.value[0]).sorted { - indexLookupTable[$0.id, default: 0] < indexLookupTable[$1.id, default: 0] - } + let sortedUniqueTargets = groupedByID.map(\.value[0]).sorted { indexLookupTable[$0.id, default: 0] < indexLookupTable[$1.id, default: 0] } targets = sortedUniqueTargets } diff --git a/Plugins/PluginsShared/PluginError.swift b/Plugins/PluginsShared/PluginError.swift index 005bb39a..aacf890b 100644 --- a/Plugins/PluginsShared/PluginError.swift +++ b/Plugins/PluginsShared/PluginError.swift @@ -118,9 +118,7 @@ private extension Array where Element == FileError { var isMisconfigurationError: Bool { // If errors for both files exist and none is a "Misconfiguration Error" then the // error can be related to a target that isn't supposed to be generator compatible at all. - if count == FileError.Kind.allCases.count, - self.allSatisfy({ !$0.issue.isMisconfigurationError }) - { + if count == FileError.Kind.allCases.count, self.allSatisfy({ !$0.issue.isMisconfigurationError }) { return false } return true From 4e37d6e2994cee193e0903830b6f628c5452df33 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Thu, 20 Jul 2023 12:18:06 +0330 Subject: [PATCH 089/100] minor change --- Plugins/OpenAPIGeneratorCommand/plugin.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Plugins/OpenAPIGeneratorCommand/plugin.swift b/Plugins/OpenAPIGeneratorCommand/plugin.swift index 9cd67756..4d067761 100644 --- a/Plugins/OpenAPIGeneratorCommand/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommand/plugin.swift @@ -54,9 +54,8 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { targets = context.package.targets } else { let matchingTargets = try context.package.targets(named: targetNameArguments) - let withDependencies = matchingTargets.flatMap { [$0] + $0.recursiveTargetDependencies } let packageTargets = Set(context.package.targets.map(\.id)) - let withLocalDependencies = withDependencies.filter { packageTargets.contains($0.id) } + let withLocalDependencies = matchingTargets.flatMap { [$0] + $0.recursiveTargetDependencies }.filter { packageTargets.contains($0.id) } let enumeratedKeyValues = withLocalDependencies.map(\.id).enumerated().map { (key: $0.element, value: $0.offset) } let indexLookupTable = Dictionary(enumeratedKeyValues, uniquingKeysWith: { l, _ in l }) let groupedByID = Dictionary(grouping: withLocalDependencies, by: \.id) From 8fb97751dea551fa265215e3fe49423995303ae2 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Thu, 20 Jul 2023 16:05:03 +0330 Subject: [PATCH 090/100] apply logging suggestions --- Plugins/OpenAPIGeneratorCommand/plugin.swift | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Plugins/OpenAPIGeneratorCommand/plugin.swift b/Plugins/OpenAPIGeneratorCommand/plugin.swift index 4d067761..4425c42e 100644 --- a/Plugins/OpenAPIGeneratorCommand/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommand/plugin.swift @@ -70,26 +70,29 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { var hadASuccessfulRun = false for target in targets { + print("Considering target '\(target.name)':") guard let swiftTarget = target as? SwiftSourceModuleTarget else { - print("- Target '\(target.name)': Not a Swift source module. Can't generate OpenAPI code.") + print("- Not a swift source module. Can't generate OpenAPI code.") continue } do { + print("- Trying OpenAPI code generation.") try runCommand( targetWorkingDirectory: target.directory, tool: context.tool, sourceFiles: swiftTarget.sourceFiles, targetName: target.name ) - print("- Target '\(target.name)': OpenAPI code generation successfully completed.") + print("- OpenAPI code generation successfully completed.") hadASuccessfulRun = true } catch let error as PluginError { if error.isMisconfigurationError { + print("- OpenAPI code generation failed with error.") throw error } else { let fileNames = FileError.Kind.allCases.map(\.name) .joined(separator: ", ", lastSeparator: " or ") - print("- Target '\(target.name)': OpenAPI code generation failed. No expected \(fileNames) files.") + print("- Skipping OpenAPI code generation. No expected \(fileNames) files.") } } } From e9be1e78eeed78d74d49fa081896192198568360 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Thu, 20 Jul 2023 16:31:14 +0330 Subject: [PATCH 091/100] Update plugin.swift --- Plugins/OpenAPIGeneratorCommand/plugin.swift | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Plugins/OpenAPIGeneratorCommand/plugin.swift b/Plugins/OpenAPIGeneratorCommand/plugin.swift index 4425c42e..e24f6cac 100644 --- a/Plugins/OpenAPIGeneratorCommand/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommand/plugin.swift @@ -90,9 +90,7 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { print("- OpenAPI code generation failed with error.") throw error } else { - let fileNames = FileError.Kind.allCases.map(\.name) - .joined(separator: ", ", lastSeparator: " or ") - print("- Skipping OpenAPI code generation. No expected \(fileNames) files.") + print("- Stopping because target isn't configured for OpenAPI code generation.") } } } From 29b63012f7b17fed12e71c0d2d6150f3e5f6c269 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Thu, 20 Jul 2023 16:33:24 +0330 Subject: [PATCH 092/100] =?UTF-8?q?try=20=E2=9C=85=20in=20logs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plugins/OpenAPIGeneratorCommand/plugin.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/OpenAPIGeneratorCommand/plugin.swift b/Plugins/OpenAPIGeneratorCommand/plugin.swift index e24f6cac..644f0fdf 100644 --- a/Plugins/OpenAPIGeneratorCommand/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommand/plugin.swift @@ -83,7 +83,7 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { sourceFiles: swiftTarget.sourceFiles, targetName: target.name ) - print("- OpenAPI code generation successfully completed.") + print("- ✅ OpenAPI code generation successfully completed.") hadASuccessfulRun = true } catch let error as PluginError { if error.isMisconfigurationError { From d8e5a478b03d15265bc233132e7aace88b0c9106 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Thu, 20 Jul 2023 16:37:22 +0330 Subject: [PATCH 093/100] Update plugin.swift --- Plugins/OpenAPIGeneratorCommand/plugin.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/OpenAPIGeneratorCommand/plugin.swift b/Plugins/OpenAPIGeneratorCommand/plugin.swift index 644f0fdf..199a53a2 100644 --- a/Plugins/OpenAPIGeneratorCommand/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommand/plugin.swift @@ -83,7 +83,7 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { sourceFiles: swiftTarget.sourceFiles, targetName: target.name ) - print("- ✅ OpenAPI code generation successfully completed.") + print("- ✅ OpenAPI code generation for target '\(target.name)' successfully completed .") hadASuccessfulRun = true } catch let error as PluginError { if error.isMisconfigurationError { From 1b5286fab1f256d9536f998f76bdb916a42e948f Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Thu, 20 Jul 2023 16:46:30 +0330 Subject: [PATCH 094/100] remove extra space --- Plugins/OpenAPIGeneratorCommand/plugin.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/OpenAPIGeneratorCommand/plugin.swift b/Plugins/OpenAPIGeneratorCommand/plugin.swift index 199a53a2..df57b081 100644 --- a/Plugins/OpenAPIGeneratorCommand/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommand/plugin.swift @@ -83,7 +83,7 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { sourceFiles: swiftTarget.sourceFiles, targetName: target.name ) - print("- ✅ OpenAPI code generation for target '\(target.name)' successfully completed .") + print("- ✅ OpenAPI code generation for target '\(target.name)' successfully completed.") hadASuccessfulRun = true } catch let error as PluginError { if error.isMisconfigurationError { From 7c9cee3f993c25c0179fb1b1b4f6d0e4c57c314d Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Fri, 21 Jul 2023 17:06:04 +0330 Subject: [PATCH 095/100] swift-format to the rescue of me from itself! --- Package.swift | 8 ++++++-- Plugins/OpenAPIGeneratorCommand/plugin.swift | 9 ++++++--- Plugins/PluginsShared/PluginError.swift | 18 ++++++++++++------ Plugins/PluginsShared/PluginUtils.swift | 9 +++++---- 4 files changed, 29 insertions(+), 15 deletions(-) diff --git a/Package.swift b/Package.swift index 3513259f..8256a45b 100644 --- a/Package.swift +++ b/Package.swift @@ -161,11 +161,15 @@ let package = Package( verb: "generate-code-from-openapi", description: "Generate Swift code from an OpenAPI document." ), - permissions: [.writeToPackageDirectory(reason: "To write the generated Swift files back into the source directory of the package.")] + permissions: [ + .writeToPackageDirectory( + reason: "To write the generated Swift files back into the source directory of the package." + ) + ] ), dependencies: [ "swift-openapi-generator" ] - ) + ), ] ) diff --git a/Plugins/OpenAPIGeneratorCommand/plugin.swift b/Plugins/OpenAPIGeneratorCommand/plugin.swift index df57b081..dfd63452 100644 --- a/Plugins/OpenAPIGeneratorCommand/plugin.swift +++ b/Plugins/OpenAPIGeneratorCommand/plugin.swift @@ -55,11 +55,14 @@ extension SwiftOpenAPIGeneratorPlugin: CommandPlugin { } else { let matchingTargets = try context.package.targets(named: targetNameArguments) let packageTargets = Set(context.package.targets.map(\.id)) - let withLocalDependencies = matchingTargets.flatMap { [$0] + $0.recursiveTargetDependencies }.filter { packageTargets.contains($0.id) } - let enumeratedKeyValues = withLocalDependencies.map(\.id).enumerated().map { (key: $0.element, value: $0.offset) } + let withLocalDependencies = matchingTargets.flatMap { [$0] + $0.recursiveTargetDependencies } + .filter { packageTargets.contains($0.id) } + let enumeratedKeyValues = withLocalDependencies.map(\.id).enumerated() + .map { (key: $0.element, value: $0.offset) } let indexLookupTable = Dictionary(enumeratedKeyValues, uniquingKeysWith: { l, _ in l }) let groupedByID = Dictionary(grouping: withLocalDependencies, by: \.id) - let sortedUniqueTargets = groupedByID.map(\.value[0]).sorted { indexLookupTable[$0.id, default: 0] < indexLookupTable[$1.id, default: 0] } + let sortedUniqueTargets = groupedByID.map(\.value[0]) + .sorted { indexLookupTable[$0.id, default: 0] < indexLookupTable[$1.id, default: 0] } targets = sortedUniqueTargets } diff --git a/Plugins/PluginsShared/PluginError.swift b/Plugins/PluginsShared/PluginError.swift index aacf890b..be20b7d2 100644 --- a/Plugins/PluginsShared/PluginError.swift +++ b/Plugins/PluginsShared/PluginError.swift @@ -11,14 +11,16 @@ enum PluginError: Swift.Error, CustomStringConvertible, LocalizedError { var description: String { switch self { case .incompatibleTarget(let name): - return "Incompatible target called '\(name)'. Only Swift source targets can be used with the Swift OpenAPI Generator plugin." + return + "Incompatible target called '\(name)'. Only Swift source targets can be used with the Swift OpenAPI Generator plugin." case .generatorFailure(let targetName): return "The generator failed to generate OpenAPI files for target '\(targetName)'." case .noTargetsWithExpectedFiles(let targetNames): let fileNames = FileError.Kind.allCases.map(\.name) .joined(separator: ", ", lastSeparator: " or ") let targetNames = targetNames.joined(separator: ", ", lastSeparator: " and ") - return "Targets with names \(targetNames) don't contain any \(fileNames) files with expected names. See documentation for details." + return + "Targets with names \(targetNames) don't contain any \(fileNames) files with expected names. See documentation for details." case .noTargetsMatchingTargetNames(let targetNames): let targetNames = targetNames.joined(separator: ", ", lastSeparator: " and ") return "Found no targets with names \(targetNames)." @@ -94,16 +96,20 @@ struct FileError: Swift.Error, CustomStringConvertible, LocalizedError { case .config: switch issue { case .noFilesFound: - return "No config file found in the target named '\(targetName)'. Add a file called 'openapi-generator-config.yaml' or 'openapi-generator-config.yml' to the target's source directory. See documentation for details." + return + "No config file found in the target named '\(targetName)'. Add a file called 'openapi-generator-config.yaml' or 'openapi-generator-config.yml' to the target's source directory. See documentation for details." case .multipleFilesFound(let files): - return "Multiple config files found in the target named '\(targetName)', but exactly one is expected. Found \(files.map(\.description).joined(separator: " "))." + return + "Multiple config files found in the target named '\(targetName)', but exactly one is expected. Found \(files.map(\.description).joined(separator: " "))." } case .document: switch issue { case .noFilesFound: - return "No OpenAPI document found in the target named '\(targetName)'. Add a file called 'openapi.yaml', 'openapi.yml' or 'openapi.json' (can also be a symlink) to the target's source directory. See documentation for details." + return + "No OpenAPI document found in the target named '\(targetName)'. Add a file called 'openapi.yaml', 'openapi.yml' or 'openapi.json' (can also be a symlink) to the target's source directory. See documentation for details." case .multipleFilesFound(let files): - return "Multiple OpenAPI documents found in the target named '\(targetName)', but exactly one is expected. Found \(files.map(\.description).joined(separator: " "))." + return + "Multiple OpenAPI documents found in the target named '\(targetName)', but exactly one is expected. Found \(files.map(\.description).joined(separator: " "))." } } } diff --git a/Plugins/PluginsShared/PluginUtils.swift b/Plugins/PluginsShared/PluginUtils.swift index 4ad22ce4..6f6e7f1c 100644 --- a/Plugins/PluginsShared/PluginUtils.swift +++ b/Plugins/PluginsShared/PluginUtils.swift @@ -1,7 +1,9 @@ import PackagePlugin enum PluginUtils { - private static var supportedConfigFiles: Set { Set(["yaml", "yml"].map { "openapi-generator-config." + $0 }) } + private static var supportedConfigFiles: Set { + Set(["yaml", "yml"].map { "openapi-generator-config." + $0 }) + } private static var supportedDocFiles: Set { Set(["yaml", "yml", "json"].map { "openapi." + $0 }) } /// Validated values to run a plugin with. @@ -119,10 +121,9 @@ enum PluginUtils { extension Array where Element == String { func joined(separator: String, lastSeparator: String) -> String { - if count > 1 { - return "\(self.dropLast().joined(separator: separator))\(lastSeparator)\(self.last!)" - } else { + guard count > 1 else { return self.joined(separator: separator) } + return "\(self.dropLast().joined(separator: separator))\(lastSeparator)\(self.last!)" } } From d4b7756436a54c33e0b1ca48eeff3568ddd5aa12 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Fri, 21 Jul 2023 17:17:26 +0330 Subject: [PATCH 096/100] exclude directory --- scripts/check-license-headers.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/check-license-headers.sh b/scripts/check-license-headers.sh index 06932197..365c7e1a 100644 --- a/scripts/check-license-headers.sh +++ b/scripts/check-license-headers.sh @@ -66,6 +66,7 @@ read -ra PATHS_TO_CHECK_FOR_LICENSE <<< "$( \ ":(exclude)**/petstore.yaml" \ ":(exclude)**/openapi-generator-config.yaml" \ ":(exclude)**/openapi-generator-config.yml" \ + ":(exclude)Plugins/OpenAPIGenerator/PluginsShared" \ | xargs -0 \ )" From a2b0ab46ea72d983e030df2fd79c762f5432c272 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Fri, 21 Jul 2023 17:19:18 +0330 Subject: [PATCH 097/100] another try to exclude directory from headers-check script --- scripts/check-license-headers.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check-license-headers.sh b/scripts/check-license-headers.sh index 365c7e1a..37d745ab 100644 --- a/scripts/check-license-headers.sh +++ b/scripts/check-license-headers.sh @@ -66,7 +66,7 @@ read -ra PATHS_TO_CHECK_FOR_LICENSE <<< "$( \ ":(exclude)**/petstore.yaml" \ ":(exclude)**/openapi-generator-config.yaml" \ ":(exclude)**/openapi-generator-config.yml" \ - ":(exclude)Plugins/OpenAPIGenerator/PluginsShared" \ + ":(exclude)**/Plugins/OpenAPIGenerator/PluginsShared" \ | xargs -0 \ )" From 96398f42a639b0f92263efee9957c6707eccbbb9 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Fri, 21 Jul 2023 17:31:02 +0330 Subject: [PATCH 098/100] headers script another try --- scripts/check-license-headers.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check-license-headers.sh b/scripts/check-license-headers.sh index 37d745ab..365c7e1a 100644 --- a/scripts/check-license-headers.sh +++ b/scripts/check-license-headers.sh @@ -66,7 +66,7 @@ read -ra PATHS_TO_CHECK_FOR_LICENSE <<< "$( \ ":(exclude)**/petstore.yaml" \ ":(exclude)**/openapi-generator-config.yaml" \ ":(exclude)**/openapi-generator-config.yml" \ - ":(exclude)**/Plugins/OpenAPIGenerator/PluginsShared" \ + ":(exclude)Plugins/OpenAPIGenerator/PluginsShared" \ | xargs -0 \ )" From a4f85848488fb76cfdf23e7401cad1d8be27784c Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Fri, 21 Jul 2023 17:33:01 +0330 Subject: [PATCH 099/100] fix the script itself (the check should still fail) --- scripts/check-license-headers.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/check-license-headers.sh b/scripts/check-license-headers.sh index 365c7e1a..d0e0c398 100644 --- a/scripts/check-license-headers.sh +++ b/scripts/check-license-headers.sh @@ -67,6 +67,7 @@ read -ra PATHS_TO_CHECK_FOR_LICENSE <<< "$( \ ":(exclude)**/openapi-generator-config.yaml" \ ":(exclude)**/openapi-generator-config.yml" \ ":(exclude)Plugins/OpenAPIGenerator/PluginsShared" \ + ":(exclude)Plugins/OpenAPIGeneratorCommand/PluginsShared" \ | xargs -0 \ )" From 4b4d0f89b0263ff07e117b8e2491cb34951aafdc Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Fri, 21 Jul 2023 17:35:26 +0330 Subject: [PATCH 100/100] final soundness fix hopefully? --- Plugins/PluginsShared/PluginError.swift | 13 +++++++++++++ Plugins/PluginsShared/PluginUtils.swift | 13 +++++++++++++ Sources/swift-openapi-generator/PluginSource.swift | 14 ++++++++++++++ 3 files changed, 40 insertions(+) diff --git a/Plugins/PluginsShared/PluginError.swift b/Plugins/PluginsShared/PluginError.swift index be20b7d2..604e94d1 100644 --- a/Plugins/PluginsShared/PluginError.swift +++ b/Plugins/PluginsShared/PluginError.swift @@ -1,3 +1,16 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftOpenAPIGenerator open source project +// +// Copyright (c) 2023 Apple Inc. and the SwiftOpenAPIGenerator project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftOpenAPIGenerator project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// import PackagePlugin import Foundation diff --git a/Plugins/PluginsShared/PluginUtils.swift b/Plugins/PluginsShared/PluginUtils.swift index 6f6e7f1c..a654c1db 100644 --- a/Plugins/PluginsShared/PluginUtils.swift +++ b/Plugins/PluginsShared/PluginUtils.swift @@ -1,3 +1,16 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftOpenAPIGenerator open source project +// +// Copyright (c) 2023 Apple Inc. and the SwiftOpenAPIGenerator project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftOpenAPIGenerator project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// import PackagePlugin enum PluginUtils { diff --git a/Sources/swift-openapi-generator/PluginSource.swift b/Sources/swift-openapi-generator/PluginSource.swift index 2afb05af..cf57d914 100644 --- a/Sources/swift-openapi-generator/PluginSource.swift +++ b/Sources/swift-openapi-generator/PluginSource.swift @@ -1,3 +1,17 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftOpenAPIGenerator open source project +// +// Copyright (c) 2023 Apple Inc. and the SwiftOpenAPIGenerator project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftOpenAPIGenerator project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + /// The source of a plugin generator invocation. enum PluginSource: String, Codable { /// BuildTool plugin