Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,36 +24,36 @@ import kotlin.test.assertTrue
class JavaEnumsTest {
@Test
fun exhaustiveWhen() {
val label = when (Orders.fill(OrderStatus.PENDING)) {
OrderStatus.PENDING -> "pending"
OrderStatus.FILLED -> "filled"
OrderStatus.CANCELLED -> "cancelled"
val label = when (Orders.fill(OrderStatus.pending)) {
OrderStatus.pending -> "pending"
OrderStatus.filled -> "filled"
OrderStatus.cancelled -> "cancelled"
}
assertEquals("filled", label)
}

@Test
fun constantsAreTheValuesSwiftReturns() {
assertSame(OrderStatus.FILLED, Orders.fill(OrderStatus.PENDING))
assertSame(OrderStatus.filled, Orders.fill(OrderStatus.pending))
assertEquals(OrderStatus.entries, Orders.all().toList())
assertEquals("cancelled", OrderStatus.CANCELLED.rawValue)
assertTrue(OrderStatus.CANCELLED.isFinal)
assertEquals("cancelled", OrderStatus.cancelled.rawValue)
assertTrue(OrderStatus.cancelled.isFinal)
}

@Test
fun everywhereAValueCrosses() = runBlocking {
assertSame(OrderStatus.PENDING, Orders.status("a"))
assertSame(OrderStatus.FILLED, Orders.byID()["a"])
assertSame(OrderStatus.CANCELLED, Order.init("a", OrderStatus.CANCELLED).status)
assertSame(OrderStatus.FILLED, (Orders.event(OrderStatus.FILLED).case as OrderEvent.Case.Changed).arg0)
assertSame(OrderStatus.pending, Orders.status("a"))
assertSame(OrderStatus.filled, Orders.byID()["a"])
assertSame(OrderStatus.cancelled, Order.init("a", OrderStatus.cancelled).status)
assertSame(OrderStatus.filled, (Orders.event(OrderStatus.filled).case as OrderEvent.Case.Changed).arg0)
var seen: OrderStatus? = null
Orders.watch { seen = it }
assertSame(OrderStatus.CANCELLED, seen)
assertSame(OrderStatus.FILLED, Orders.settle().await())
assertSame(OrderStatus.cancelled, seen)
assertSame(OrderStatus.filled, Orders.settle().await())
}

@Test
fun manyRoundTrips() {
repeat(10_000) { assertSame(OrderStatus.FILLED, Orders.fill(OrderStatus.FILLED)) }
repeat(10_000) { assertSame(OrderStatus.filled, Orders.fill(OrderStatus.filled)) }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ extension JNISwift2JavaGenerator {
private func printConcreteType(_ printer: inout JavaPrinter, _ decl: ExtractedNominalType) {
printNominal(&printer, decl) { printer in
if self.isJavaEnum(decl) {
printer.print(decl.cases.map { $0.name.uppercased() }.joined(separator: ", ") + ";")
printer.print(decl.cases.map { Self.javaEnumConstantName($0) }.joined(separator: ", ") + ";")
printer.println()
}
printer.print(
Expand Down Expand Up @@ -679,6 +679,12 @@ extension JNISwift2JavaGenerator {

/// Whether `decl` is printed as a Java `enum` (`payloadlessEnumsAsJavaEnums`): a non-generic,
/// non-error Swift enum whose cases have no associated values.
/// The Swift case name, as Swift and Kotlin code spell the case; a Java keyword gets a trailing `_`.
static func javaEnumConstantName(_ enumCase: ExtractedEnumCase) -> String {
let name = enumCase.name.trimmingCharacters(in: CharacterSet(charactersIn: "`"))
return JavaIdentifierFactory.javaKeywords.contains(name) ? name + "_" : name
}

func isJavaEnum(_ decl: ExtractedNominalType) -> Bool {
config.effectivePayloadlessEnumsAsJavaEnums && decl.swiftNominal.kind == .enum && !decl.swiftNominal.isGeneric
&& !decl.cases.isEmpty && decl.cases.allSatisfy(\.parameters.isEmpty) && !self.isSwiftErrorType(decl)
Expand Down
2 changes: 1 addition & 1 deletion Sources/SwiftJavaConfigurationShared/Configuration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ public struct Configuration: Codable {
}

/// If true, Swift enums whose cases have no associated values are generated as Java `enum`s,
/// with one constant per case, instead of classes wrapping a Swift value. Only supported in
/// with one constant per case named like the case, instead of classes wrapping a Swift value. Only supported in
/// `jni` mode.
public var payloadlessEnumsAsJavaEnums: Bool?
public var effectivePayloadlessEnumsAsJavaEnums: Bool {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ extract or translate, and lists each one. Skipped declarations are always report
- **Default:** `false`

If true, Swift enums whose cases have no associated values are generated as Java `enum`s,
with one constant per case, instead of classes wrapping a Swift value. Only supported in
with one constant per case named like the case, instead of classes wrapping a Swift value. Only supported in
`jni` mode.

---
Expand Down
20 changes: 19 additions & 1 deletion Tests/JExtractSwiftTests/JNI/JNIJavaEnumTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,23 @@ struct JNIJavaEnumTests {
}
"""

@Test
func javaKeywordCaseIsEscaped() throws {
try assertOutput(
input: """
public enum Policy {
case `default`
case strict
}
""",
config: config,
.jni,
.java,
detectChunkByInitialLines: 1,
expectedChunks: ["default_, strict;"]
)
}

@Test
func casesBecomeConstants() throws {
try assertOutput(
Expand All @@ -42,7 +59,8 @@ struct JNIJavaEnumTests {
detectChunkByInitialLines: 1,
expectedChunks: [
"public enum Phase implements JNISwiftInstance {",
"IDLE, LOADING;",
// Swift case names, as Swift and Kotlin code spell them.
"idle, loading;",
"static final long[] ADDRESSES = { Phase.$idle(), Phase.$loading() };",
"""
public static Phase wrapMemoryAddressUnsafe(long selfPointer) {
Expand Down
Loading