Skip to content

Commit 13ddcf3

Browse files
committed
review comments, rethink config & imports
1 parent 87197c6 commit 13ddcf3

File tree

5 files changed

+311
-202
lines changed

5 files changed

+311
-202
lines changed

Plugins/GRPCProtobufGenerator/BuildPluginConfig.swift

Lines changed: 160 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -18,110 +18,184 @@ import Foundation
1818

1919
let configFileName = "grpc-swift-proto-generator-config.json"
2020

21-
/// The configuration of the build plugin.
21+
/// The config of the build plugin.
2222
struct BuildPluginConfig: Codable {
23-
/// The visibility of the generated files.
24-
///
25-
/// Defaults to `Internal`.
26-
var visibility: GenerationConfig.Visibility
27-
/// Whether server code is generated.
28-
///
29-
/// Defaults to `true`.
30-
var server: Bool
31-
/// Whether client code is generated.
32-
///
33-
/// Defaults to `true`.
34-
var client: Bool
35-
/// Whether message code is generated.
36-
///
37-
/// Defaults to `true`.
38-
var message: Bool
39-
/// Whether imports should have explicit access levels.
40-
///
41-
/// Defaults to `false`.
42-
var useAccessLevelOnImports: Bool
43-
44-
/// Specify the directory in which to search for imports.
45-
///
46-
/// Paths are relative to the location of the specifying config file.
47-
/// Build plugins only have access to files within the target's source directory.
48-
/// May be specified multiple times; directories will be searched in order.
49-
/// The target source directory is always appended
50-
/// to the import paths.
51-
var importPaths: [String]
52-
53-
/// The path to the `protoc` binary.
54-
///
55-
/// If this is not set, Swift Package Manager will try to find the tool itself.
56-
var protocPath: String?
23+
/// Config defining which components should be considered when generating source.
24+
struct Generate {
25+
/// Whether server code is generated.
26+
///
27+
/// Defaults to `true`.
28+
var servers: Bool
29+
/// Whether client code is generated.
30+
///
31+
/// Defaults to `true`.
32+
var clients: Bool
33+
/// Whether message code is generated.
34+
///
35+
/// Defaults to `true`.
36+
var messages: Bool
37+
38+
static let defaults = Self(
39+
servers: true,
40+
clients: true,
41+
messages: true
42+
)
43+
44+
private init(servers: Bool, clients: Bool, messages: Bool) {
45+
self.servers = servers
46+
self.clients = clients
47+
self.messages = messages
48+
}
49+
}
50+
51+
/// Config relating to the generated code itself.
52+
struct GeneratedSource {
53+
/// The visibility of the generated files.
54+
///
55+
/// Defaults to `Internal`.
56+
var accessLevel: GenerationConfig.AccessLevel
57+
/// Whether imports should have explicit access levels.
58+
///
59+
/// Defaults to `false`.
60+
var useAccessLevelOnImports: Bool
61+
62+
static let defaults = Self(
63+
accessLevel: .internal,
64+
useAccessLevelOnImports: false
65+
)
66+
67+
private init(accessLevel: GenerationConfig.AccessLevel, useAccessLevelOnImports: Bool) {
68+
self.accessLevel = accessLevel
69+
self.useAccessLevelOnImports = useAccessLevelOnImports
70+
}
71+
}
72+
73+
/// Config relating to the protoc invocation.
74+
struct Protoc {
75+
/// Specify the directory in which to search for imports.
76+
///
77+
/// Paths are relative to the location of the specifying config file.
78+
/// Build plugins only have access to files within the target's source directory.
79+
/// May be specified multiple times; directories will be searched in order.
80+
/// The target source directory is always appended
81+
/// to the import paths.
82+
var importPaths: [String]
83+
84+
/// The path to the `protoc` executable binary.
85+
///
86+
/// If this is not set, Swift Package Manager will try to find the tool itself.
87+
var executablePath: String?
88+
89+
static let defaults = Self(
90+
importPaths: [],
91+
executablePath: nil
92+
)
93+
94+
private init(importPaths: [String], executablePath: String?) {
95+
self.importPaths = importPaths
96+
self.executablePath = executablePath
97+
}
98+
}
99+
100+
/// Config defining which components should be considered when generating source.
101+
var generate: Generate
102+
/// Config relating to the nature of the generated code.
103+
var generatedSource: GeneratedSource
104+
/// Config relating to the protoc invocation.
105+
var protoc: Protoc
106+
107+
static let defaults = Self(
108+
generate: Generate.defaults,
109+
generatedSource: GeneratedSource.defaults,
110+
protoc: Protoc.defaults
111+
)
112+
private init(generate: Generate, generatedSource: GeneratedSource, protoc: Protoc) {
113+
self.generate = generate
114+
self.generatedSource = generatedSource
115+
self.protoc = protoc
116+
}
57117

58118
// Codable conformance with defaults
59119
enum CodingKeys: String, CodingKey {
60-
case visibility
61-
case server
62-
case client
63-
case message
120+
case generate
121+
case generatedSource
122+
case protoc
123+
}
124+
125+
126+
init(from decoder: any Decoder) throws {
127+
let container = try decoder.container(keyedBy: CodingKeys.self)
128+
129+
self.generate = try container.decodeIfPresent(Generate.self, forKey: .generate) ?? Self.defaults.generate
130+
self.generatedSource = try container.decodeIfPresent(GeneratedSource.self, forKey: .generatedSource) ?? Self.defaults.generatedSource
131+
self.protoc = try container.decodeIfPresent(Protoc.self, forKey: .protoc) ?? Self.defaults.protoc
132+
}
133+
}
134+
135+
extension BuildPluginConfig.Generate: Codable {
136+
// Codable conformance with defaults
137+
enum CodingKeys: String, CodingKey {
138+
case servers
139+
case clients
140+
case messages
141+
}
142+
143+
init(from decoder: any Decoder) throws {
144+
let container = try decoder.container(keyedBy: CodingKeys.self)
145+
146+
self.servers = try container.decodeIfPresent(Bool.self, forKey: .servers) ?? Self.defaults.servers
147+
self.clients = try container.decodeIfPresent(Bool.self, forKey: .clients) ?? Self.defaults.clients
148+
self.messages = try container.decodeIfPresent(Bool.self, forKey: .messages) ?? Self.defaults.messages
149+
}
150+
}
151+
152+
extension BuildPluginConfig.GeneratedSource: Codable {
153+
// Codable conformance with defaults
154+
enum CodingKeys: String, CodingKey {
155+
case accessLevel
64156
case useAccessLevelOnImports
65-
case importPaths
66-
case protocPath
67157
}
68158

69-
let defaultVisibility: GenerationConfig.Visibility = .internal
70-
let defaultServer = true
71-
let defaultClient = true
72-
let defaultMessage = true
73-
let defaultUseAccessLevelOnImports = false
74-
let defaultImportPaths: [String] = []
159+
init(from decoder: any Decoder) throws {
160+
let container = try decoder.container(keyedBy: CodingKeys.self)
161+
162+
self.accessLevel = try container.decodeIfPresent(GenerationConfig.AccessLevel.self, forKey: .accessLevel)
163+
?? Self.defaults.accessLevel
164+
self.useAccessLevelOnImports = try container.decodeIfPresent(Bool.self,forKey: .useAccessLevelOnImports)
165+
?? Self.defaults.useAccessLevelOnImports
166+
}
167+
}
168+
169+
extension BuildPluginConfig.Protoc: Codable {
170+
// Codable conformance with defaults
171+
enum CodingKeys: String, CodingKey {
172+
case importPaths
173+
case executablePath
174+
}
75175

76176
init(from decoder: any Decoder) throws {
77177
let container = try decoder.container(keyedBy: CodingKeys.self)
78178

79-
self.visibility =
80-
try container.decodeIfPresent(GenerationConfig.Visibility.self, forKey: .visibility)
81-
?? defaultVisibility
82-
self.server = try container.decodeIfPresent(Bool.self, forKey: .server) ?? defaultServer
83-
self.client = try container.decodeIfPresent(Bool.self, forKey: .client) ?? defaultClient
84-
self.message = try container.decodeIfPresent(Bool.self, forKey: .message) ?? defaultMessage
85-
self.useAccessLevelOnImports =
86-
try container.decodeIfPresent(Bool.self, forKey: .useAccessLevelOnImports)
87-
?? defaultUseAccessLevelOnImports
88-
self.importPaths =
89-
try container.decodeIfPresent([String].self, forKey: .importPaths) ?? defaultImportPaths
90-
self.protocPath = try container.decodeIfPresent(String.self, forKey: .protocPath)
179+
self.importPaths = try container.decodeIfPresent([String].self,forKey: .importPaths)
180+
?? Self.defaults.importPaths
181+
self.executablePath = try container.decodeIfPresent(String.self, forKey: .executablePath)
91182
}
92183
}
93184

94185
extension GenerationConfig {
95-
init(configurationFile: BuildPluginConfig, configurationFilePath: URL, outputPath: URL) {
96-
self.visibility = configurationFile.visibility
97-
self.server = configurationFile.server
98-
self.client = configurationFile.client
99-
self.message = configurationFile.message
186+
init(buildPluginConfig: BuildPluginConfig, configFilePath: URL, outputPath: URL) {
187+
self.server = buildPluginConfig.generate.servers
188+
self.client = buildPluginConfig.generate.clients
189+
self.message = buildPluginConfig.generate.messages
100190
// hard-code full-path to avoid collisions since this goes into a temporary directory anyway
101191
self.fileNaming = .fullPath
102-
self.useAccessLevelOnImports = configurationFile.useAccessLevelOnImports
103-
self.importPaths = []
104-
192+
self.visibility = buildPluginConfig.generatedSource.accessLevel
193+
self.useAccessLevelOnImports = buildPluginConfig.generatedSource.useAccessLevelOnImports
105194
// Generate absolute paths for the imports relative to the config file in which they are specified
106-
self.importPaths = configurationFile.importPaths.map { relativePath in
107-
configurationFilePath.deletingLastPathComponent().relativePath + "/" + relativePath
108-
}
109-
self.protocPath = configurationFile.protocPath
110-
self.outputPath = outputPath.relativePath
111-
}
112-
}
113-
114-
extension GenerationConfig.Visibility: Codable {
115-
init?(rawValue: String) {
116-
switch rawValue.lowercased() {
117-
case "internal":
118-
self = .internal
119-
case "public":
120-
self = .public
121-
case "package":
122-
self = .package
123-
default:
124-
return nil
195+
self.importPaths = buildPluginConfig.protoc.importPaths.map { relativePath in
196+
configFilePath.deletingLastPathComponent().absoluteStringNoScheme + "/" + relativePath
125197
}
198+
self.protocPath = buildPluginConfig.protoc.executablePath
199+
self.outputPath = outputPath.absoluteStringNoScheme
126200
}
127201
}

0 commit comments

Comments
 (0)