Skip to content

Commit 0af8c51

Browse files
committed
Fix casing of generated services and methods
Motivation: Given a method name, say `ImportCSV`, from a `.proto` file, the generated method name in upper and lower camel case is `ImportCsv` and `importCsv` respectively. The generated method name (which is already expected to be in upper camel case) in upper and lower camel case should be `ImportCSV` and `importCSV` respectively. Modifications: - Replace the method used for generating service and method names in lower camel case with a newly implemented method. - Do not convert the base names of services and methods to upper camel case as they are expected to already be in upper camel case. Result: The casing of service and method names will be preserved when generating stubs.
1 parent 424a4d8 commit 0af8c51

File tree

3 files changed

+96
-5
lines changed

3 files changed

+96
-5
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright 2024, gRPC Authors All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package struct CamelCaser {
18+
/// Converts a string from upper camel case to lower camel case.
19+
package static func toLowerCamelCase(_ s: String) -> String {
20+
if s.isEmpty { return "" }
21+
22+
let indexOfFirstLowerCase = s.firstIndex(where: { $0 != "_" && $0.lowercased() == String($0) })
23+
24+
if let indexOfFirstLowerCase {
25+
if indexOfFirstLowerCase == s.startIndex {
26+
// `s` already begins with a lower case letter. As in: "importCSV".
27+
return s
28+
} else if indexOfFirstLowerCase == s.index(after: s.startIndex) {
29+
// The second character in `s` is lower case. As in: "ImportCSV".
30+
return s[s.startIndex].lowercased() + s[indexOfFirstLowerCase...] // -> "importCSV"
31+
} else {
32+
// The first lower case character is further within `s`. Tentatively, `s` begins with one or
33+
// more abbreviations. Therefore, the last encountered upper case character could be the
34+
// beginning of the next word. As in: "FOOBARImportCSV".
35+
36+
let leadingAbbreviation = s[..<s.index(before: indexOfFirstLowerCase)]
37+
let followingWords = s[s.index(before: indexOfFirstLowerCase)...]
38+
39+
return leadingAbbreviation.lowercased() + followingWords // -> "foobarImportCSV"
40+
}
41+
} else {
42+
// `s` did not contain any lower case letter.
43+
return s.lowercased()
44+
}
45+
}
46+
}

Sources/GRPCProtobufCodeGen/ProtobufCodeGenParser.swift

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
*/
1616

1717
internal import Foundation
18-
internal import SwiftProtobuf
1918
internal import SwiftProtobufPluginLibrary
2019

2120
internal import struct GRPCCodeGen.CodeGenerationRequest
@@ -125,8 +124,8 @@ extension CodeGenerationRequest.ServiceDescriptor {
125124
}
126125
let name = CodeGenerationRequest.Name(
127126
base: descriptor.name,
128-
generatedUpperCase: NamingUtils.toUpperCamelCase(descriptor.name),
129-
generatedLowerCase: NamingUtils.toLowerCamelCase(descriptor.name)
127+
generatedUpperCase: descriptor.name, // The service name from the '.proto' file is expected to be in upper camel case
128+
generatedLowerCase: CamelCaser.toLowerCamelCase(descriptor.name)
130129
)
131130

132131
// Packages that are based on the path of the '.proto' file usually
@@ -145,8 +144,8 @@ extension CodeGenerationRequest.ServiceDescriptor.MethodDescriptor {
145144
fileprivate init(descriptor: MethodDescriptor, protobufNamer: SwiftProtobufNamer) {
146145
let name = CodeGenerationRequest.Name(
147146
base: descriptor.name,
148-
generatedUpperCase: NamingUtils.toUpperCamelCase(descriptor.name),
149-
generatedLowerCase: NamingUtils.toLowerCamelCase(descriptor.name)
147+
generatedUpperCase: descriptor.name, // The method name from the '.proto' file is expected to be in upper camel case
148+
generatedLowerCase: CamelCaser.toLowerCamelCase(descriptor.name)
150149
)
151150
let documentation = descriptor.protoSourceComments()
152151
self.init(
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright 2024, gRPC Authors All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import GRPCProtobufCodeGen
18+
import Testing
19+
20+
@Suite("CamelCaser")
21+
struct CamelCaserTests {
22+
@Test(
23+
"Convert to lower camel case",
24+
arguments: [
25+
("ImportCsv", "importCsv"),
26+
("ImportCSV", "importCSV"),
27+
("CSVImport", "csvImport"),
28+
("importCSV", "importCSV"),
29+
("FOOBARImport", "foobarImport"),
30+
("FOO_BARImport", "foo_barImport"),
31+
("My_CSVImport", "my_CSVImport"),
32+
("_CSVImport", "_csvImport"),
33+
("V2Request", "v2Request"),
34+
("V2_Request", "v2_Request"),
35+
("CSV", "csv"),
36+
("I", "i"),
37+
("i", "i"),
38+
("I_", "i_"),
39+
("_", "_"),
40+
("", ""),
41+
]
42+
)
43+
func toLowerCamelCase(_ input: String, expectedOutput: String) async throws {
44+
#expect(CamelCaser.toLowerCamelCase(input) == expectedOutput)
45+
}
46+
}

0 commit comments

Comments
 (0)