Skip to content

Commit bdb7235

Browse files
committed
review comments
1 parent 9536932 commit bdb7235

File tree

3 files changed

+133
-62
lines changed

3 files changed

+133
-62
lines changed

Plugins/GRPCProtobufGeneratorCommand/CommandConfig.swift

Lines changed: 52 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -40,68 +40,77 @@ struct CommandConfig {
4040
}
4141

4242
extension CommandConfig {
43-
static func helpRequested(
44-
argumentExtractor: inout ArgumentExtractor
45-
) -> Bool {
46-
let help = argumentExtractor.extractFlag(named: OptionsAndFlags.help.rawValue)
47-
return help != 0
48-
}
49-
5043
static func parse(
5144
argumentExtractor argExtractor: inout ArgumentExtractor,
5245
pluginWorkDirectory: URL
53-
) throws -> (CommandConfig, [String]) {
46+
) throws -> CommandConfig {
5447
var config = CommandConfig.defaults
5548

5649
for flag in OptionsAndFlags.allCases {
5750
switch flag {
5851
case .accessLevel:
59-
let accessLevel = argExtractor.extractOption(named: flag.rawValue)
60-
if let value = extractSingleValue(flag, values: accessLevel) {
52+
if let value = argExtractor.extractSingleOption(named: flag.rawValue) {
6153
if let accessLevel = GenerationConfig.AccessLevel(rawValue: value) {
6254
config.common.accessLevel = accessLevel
6355
} else {
64-
Diagnostics.error("Unknown access level '--\(flag.rawValue)' \(value)")
56+
throw CommandPluginError.unknownAccessLevel(value)
6557
}
6658
}
6759

68-
case .servers, .noServers:
69-
if flag == .noServers { continue } // only process this once
60+
case .noServers:
61+
// Handled by `.servers`
62+
continue
63+
case .servers:
7064
let servers = argExtractor.extractFlag(named: OptionsAndFlags.servers.rawValue)
7165
let noServers = argExtractor.extractFlag(named: OptionsAndFlags.noServers.rawValue)
72-
if noServers > servers {
66+
if servers > 0 && noServers > 0 {
67+
throw CommandPluginError.conflictingFlags(OptionsAndFlags.servers.rawValue, OptionsAndFlags.noServers.rawValue)
68+
} else if servers > 0 {
69+
config.common.servers = true
70+
} else if noServers > 0 {
7371
config.common.servers = false
7472
}
7573

76-
case .clients, .noClients:
77-
if flag == .noClients { continue } // only process this once
74+
case .noClients:
75+
// Handled by `.clients`
76+
continue
77+
case .clients:
7878
let clients = argExtractor.extractFlag(named: OptionsAndFlags.clients.rawValue)
7979
let noClients = argExtractor.extractFlag(named: OptionsAndFlags.noClients.rawValue)
80-
if noClients > clients {
80+
if clients > 0 && noClients > 0 {
81+
throw CommandPluginError.conflictingFlags(OptionsAndFlags.clients.rawValue, OptionsAndFlags.noClients.rawValue)
82+
} else if clients > 0 {
83+
config.common.clients = true
84+
} else if noClients > 0 {
8185
config.common.clients = false
8286
}
8387

84-
case .messages, .noMessages:
85-
if flag == .noMessages { continue } // only process this once
88+
case .noMessages:
89+
// Handled by `.messages`
90+
continue
91+
case .messages:
8692
let messages = argExtractor.extractFlag(named: OptionsAndFlags.messages.rawValue)
8793
let noMessages = argExtractor.extractFlag(named: OptionsAndFlags.noMessages.rawValue)
88-
if noMessages > messages {
94+
if messages > 0 && noMessages > 0 {
95+
throw CommandPluginError.conflictingFlags(OptionsAndFlags.messages.rawValue, OptionsAndFlags.noMessages.rawValue)
96+
} else if messages > 0 {
97+
config.common.messages = true
98+
} else if noMessages > 0 {
8999
config.common.messages = false
90100
}
91101

92102
case .fileNaming:
93-
let fileNaming = argExtractor.extractOption(named: flag.rawValue)
94-
if let value = extractSingleValue(flag, values: fileNaming) {
103+
104+
if let value = argExtractor.extractSingleOption(named: flag.rawValue) {
95105
if let fileNaming = GenerationConfig.FileNaming(rawValue: value) {
96106
config.common.fileNaming = fileNaming
97107
} else {
98-
Diagnostics.error("Unknown file naming strategy '--\(flag.rawValue)' \(value)")
108+
throw CommandPluginError.unknownFileNamingStrategy(value)
99109
}
100110
}
101111

102112
case .accessLevelOnImports:
103-
let accessLevelOnImports = argExtractor.extractOption(named: flag.rawValue)
104-
if let value = extractSingleValue(flag, values: accessLevelOnImports) {
113+
if let value = argExtractor.extractSingleOption(named: flag.rawValue) {
105114
guard let accessLevelOnImports = Bool(value) else {
106115
throw CommandPluginError.invalidArgumentValue(name: flag.rawValue, value: value)
107116
}
@@ -112,13 +121,10 @@ extension CommandConfig {
112121
config.common.importPaths = argExtractor.extractOption(named: flag.rawValue)
113122

114123
case .protocPath:
115-
let protocPath = argExtractor.extractOption(named: flag.rawValue)
116-
config.common.protocPath = extractSingleValue(flag, values: protocPath)
124+
config.common.protocPath = argExtractor.extractSingleOption(named: flag.rawValue)
117125

118-
case .output:
119-
let output = argExtractor.extractOption(named: flag.rawValue)
120-
config.common.outputPath =
121-
extractSingleValue(flag, values: output) ?? pluginWorkDirectory.absoluteStringNoScheme
126+
case .outputPath:
127+
config.common.outputPath = argExtractor.extractSingleOption(named: flag.rawValue) ?? pluginWorkDirectory.absoluteStringNoScheme
122128

123129
case .verbose:
124130
let verbose = argExtractor.extractFlag(named: flag.rawValue)
@@ -133,27 +139,24 @@ extension CommandConfig {
133139
}
134140
}
135141

136-
if argExtractor.remainingArguments.isEmpty {
137-
throw CommandPluginError.missingInputFile
138-
}
139-
140-
for argument in argExtractor.remainingArguments {
141-
if argument.hasPrefix("--") {
142-
throw CommandPluginError.unknownOption(argument)
143-
}
142+
if let argument = argExtractor.remainingArguments.first {
143+
throw CommandPluginError.unknownOption(argument)
144144
}
145145

146-
return (config, argExtractor.remainingArguments)
146+
return config
147147
}
148148
}
149149

150-
func extractSingleValue(_ flag: OptionsAndFlags, values: [String]) -> String? {
151-
if values.count > 1 {
152-
Stderr.print(
153-
"Warning: '--\(flag.rawValue)' was unexpectedly repeated, the first value will be used."
154-
)
150+
extension ArgumentExtractor {
151+
mutating func extractSingleOption(named optionName: String) -> String? {
152+
let values = self.extractOption(named: optionName)
153+
if values.count > 1 {
154+
Diagnostics.warning(
155+
"'--\(optionName)' was unexpectedly repeated, the first value will be used."
156+
)
157+
}
158+
return values.first
155159
}
156-
return values.first
157160
}
158161

159162
/// All valid input options/flags
@@ -169,7 +172,7 @@ enum OptionsAndFlags: String, CaseIterable {
169172
case accessLevelOnImports = "access-level-on-imports"
170173
case importPath = "import-path"
171174
case protocPath = "protoc-path"
172-
case output
175+
case outputPath = "output-path"
173176
case verbose
174177
case dryRun = "dry-run"
175178

@@ -205,7 +208,7 @@ extension OptionsAndFlags {
205208
return "The path to the protoc binary."
206209
case .dryRun:
207210
return "Print but do not execute the protoc commands."
208-
case .output:
211+
case .outputPath:
209212
return "The path into which the generated source files are created."
210213
case .verbose:
211214
return "Emit verbose output."
@@ -222,7 +225,7 @@ extension OptionsAndFlags {
222225
printMessage = Stderr.print
223226
}
224227

225-
printMessage("Usage: swift package generate-grpc-code-from-protos [flags] [input files]")
228+
printMessage("Usage: swift package generate-grpc-code-from-protos [flags] [--] [input files]")
226229
printMessage("")
227230
printMessage("Flags:")
228231
printMessage("")

Plugins/GRPCProtobufGeneratorCommand/CommandPluginError.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ enum CommandPluginError: Error {
1919
case invalidArgumentValue(name: String, value: String)
2020
case missingInputFile
2121
case unknownOption(String)
22+
case unknownAccessLevel(String)
23+
case unknownFileNamingStrategy(String)
24+
case conflictingFlags(String, String)
25+
case generationFailure
26+
case tooManyParameterSeparators
2227
}
2328

2429
extension CommandPluginError: CustomStringConvertible {
@@ -32,6 +37,16 @@ extension CommandPluginError: CustomStringConvertible {
3237
"No input file(s) specified."
3338
case .unknownOption(let value):
3439
"Provided option is unknown: \(value)."
40+
case .unknownAccessLevel(let value):
41+
"Provided access level is unknown: \(value)."
42+
case .unknownFileNamingStrategy(let value):
43+
"Provided file naming strategy is unknown: \(value)."
44+
case .conflictingFlags(let flag1, let flag2):
45+
"Provided flags conflict: '\(flag1)' and '\(flag2)'."
46+
case .generationFailure:
47+
"Code generation failed."
48+
case .tooManyParameterSeparators:
49+
"Unexpected parameter structure, too many '--' separators."
3550
}
3651
}
3752
}

Plugins/GRPCProtobufGeneratorCommand/Plugin.swift

Lines changed: 66 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,27 +17,80 @@
1717
import Foundation
1818
import PackagePlugin
1919

20-
@main
21-
struct GRPCProtobufGeneratorCommandPlugin: CommandPlugin {
20+
21+
extension GRPCProtobufGeneratorCommandPlugin: CommandPlugin {
2222
/// Perform command, the entry-point when using a Package manifest.
2323
func performCommand(context: PluginContext, arguments: [String]) async throws {
24-
var argExtractor = ArgumentExtractor(arguments)
24+
try self.performCommand(
25+
arguments: arguments,
26+
tool: context.tool,
27+
pluginWorkDirectoryURL: context.pluginWorkDirectoryURL
28+
)
29+
}
30+
}
31+
32+
#if canImport(XcodeProjectPlugin)
33+
import XcodeProjectPlugin
2534

26-
if CommandConfig.helpRequested(argumentExtractor: &argExtractor) {
35+
// Entry-point when using Xcode projects
36+
extension GRPCProtobufGeneratorCommandPlugin: XcodeCommandPlugin {
37+
/// Perform command, the entry-point when using an Xcode project.
38+
func performCommand(context: XcodeProjectPlugin.XcodePluginContext, arguments: [String]) throws {
39+
try self.performCommand(
40+
arguments: arguments,
41+
tool: context.tool,
42+
pluginWorkDirectoryURL: context.pluginWorkDirectoryURL
43+
)
44+
}
45+
}
46+
#endif
47+
48+
@main
49+
struct GRPCProtobufGeneratorCommandPlugin {
50+
/// Command plugin code common to both invocation types: package manifest Xcode project
51+
func performCommand(arguments: [String], tool: (String) throws -> PluginContext.Tool, pluginWorkDirectoryURL: URL) throws {
52+
let groups = arguments.split(separator: "--")
53+
let flagsAndOptions: [String]
54+
let inputFiles: [String]
55+
switch groups.count {
56+
case 0:
57+
OptionsAndFlags.printHelp(requested: false)
58+
return
59+
60+
case 1:
61+
inputFiles = Array(groups[0])
62+
flagsAndOptions = []
63+
64+
var argExtractor = ArgumentExtractor(inputFiles)
65+
// check if help requested
66+
if argExtractor.extractFlag(named: OptionsAndFlags.help.rawValue) > 0 {
67+
OptionsAndFlags.printHelp(requested: true)
68+
return
69+
}
70+
71+
case 2:
72+
flagsAndOptions = Array(groups[0])
73+
inputFiles = Array(groups[1])
74+
75+
default:
76+
throw CommandPluginError.tooManyParameterSeparators
77+
}
78+
79+
var argExtractor = ArgumentExtractor(flagsAndOptions)
80+
// help requested
81+
if argExtractor.extractFlag(named: OptionsAndFlags.help.rawValue) > 0 {
2782
OptionsAndFlags.printHelp(requested: true)
2883
return
2984
}
3085

3186
// MARK: Configuration
3287
let commandConfig: CommandConfig
33-
let inputFiles: [String]
3488
do {
35-
(commandConfig, inputFiles) = try CommandConfig.parse(
89+
commandConfig = try CommandConfig.parse(
3690
argumentExtractor: &argExtractor,
37-
pluginWorkDirectory: context.pluginWorkDirectoryURL
91+
pluginWorkDirectory: pluginWorkDirectoryURL
3892
)
3993
} catch {
40-
OptionsAndFlags.printHelp(requested: false)
4194
throw error
4295
}
4396

@@ -46,9 +99,9 @@ struct GRPCProtobufGeneratorCommandPlugin: CommandPlugin {
4699
}
47100

48101
let config = commandConfig.common
49-
let protocPath = try deriveProtocPath(using: config, tool: context.tool)
50-
let protocGenGRPCSwiftPath = try context.tool(named: "protoc-gen-grpc-swift").url
51-
let protocGenSwiftPath = try context.tool(named: "protoc-gen-swift").url
102+
let protocPath = try deriveProtocPath(using: config, tool: tool)
103+
let protocGenGRPCSwiftPath = try tool("protoc-gen-grpc-swift").url
104+
let protocGenSwiftPath = try tool("protoc-gen-swift").url
52105

53106
let outputDirectory = URL(fileURLWithPath: config.outputPath)
54107
if commandConfig.verbose {
@@ -83,7 +136,7 @@ struct GRPCProtobufGeneratorCommandPlugin: CommandPlugin {
83136
}
84137
} else {
85138
let problem = "\(process.terminationReason):\(process.terminationStatus)"
86-
Diagnostics.error("Generating gRPC Swift files failed: \(problem)")
139+
throw CommandPluginError.generationFailure
87140
}
88141
}
89142
}
@@ -112,7 +165,7 @@ struct GRPCProtobufGeneratorCommandPlugin: CommandPlugin {
112165
)
113166
} else {
114167
let problem = "\(process.terminationReason):\(process.terminationStatus)"
115-
Diagnostics.error("Generating Protobuf message Swift files failed: \(problem)")
168+
throw CommandPluginError.generationFailure
116169
}
117170
}
118171
}

0 commit comments

Comments
 (0)