From e5bcf6eb1ce7a66ff830390d738b864664f99c27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=98yvind=20Raddum=20Berg?= Date: Sat, 31 Jan 2026 23:41:09 +0100 Subject: [PATCH] Add gRPC code generation with effect type support, Kotlin-Quarkus-Mutiny tester, and full roundtrip testing Co-Authored-By: Claude Opus 4.5 --- bleep.yaml | 87 ++ settings.gradle.kts | 6 + .../com/example/grpc/BankTransfer.java | 74 ++ .../com/example/grpc/ChatMessage.java | 114 ++ .../com/example/grpc/CreateOrderRequest.java | 80 ++ .../com/example/grpc/CreateOrderResponse.java | 74 ++ .../com/example/grpc/CreditCard.java | 83 ++ .../com/example/grpc/Customer.java | 83 ++ .../com/example/grpc/CustomerId.java | 23 + .../com/example/grpc/EchoService.java | 22 + .../com/example/grpc/EchoServiceClient.java | 127 ++ .../com/example/grpc/EchoServiceServer.java | 145 +++ .../com/example/grpc/GetCustomerRequest.java | 65 + .../com/example/grpc/GetCustomerResponse.java | 80 ++ .../com/example/grpc/Inner.java | 74 ++ .../com/example/grpc/Inventory.java | 159 +++ .../com/example/grpc/ListOrdersRequest.java | 74 ++ .../com/example/grpc/Notification.java | 102 ++ .../com/example/grpc/NotificationTarget.java | 23 + .../com/example/grpc/OptionalFields.java | 119 ++ .../com/example/grpc/Order.java | 123 ++ .../com/example/grpc/OrderId.java | 23 + .../com/example/grpc/OrderService.java | 16 + .../com/example/grpc/OrderServiceClient.java | 77 ++ .../com/example/grpc/OrderServiceServer.java | 95 ++ .../com/example/grpc/OrderStatus.java | 73 ++ .../com/example/grpc/OrderSummary.java | 74 ++ .../com/example/grpc/OrderUpdate.java | 114 ++ .../com/example/grpc/Outer.java | 89 ++ .../com/example/grpc/PaymentMethod.java | 132 ++ .../com/example/grpc/PaymentMethodMethod.java | 25 + .../com/example/grpc/Priority.java | 68 ++ .../com/example/grpc/ScalarTypes.java | 447 +++++++ .../com/example/grpc/Wallet.java | 74 ++ .../example/grpc/WellKnownTypesMessage.java | 226 ++++ .../com/example/grpc/GrpcIntegrationTest.java | 520 ++++++++ .../com/example/grpc/BankTransfer.java | 74 ++ .../com/example/grpc/ChatMessage.java | 114 ++ .../com/example/grpc/CreateOrderRequest.java | 80 ++ .../com/example/grpc/CreateOrderResponse.java | 74 ++ .../com/example/grpc/CreditCard.java | 83 ++ .../com/example/grpc/Customer.java | 83 ++ .../com/example/grpc/CustomerId.java | 23 + .../com/example/grpc/EchoService.java | 22 + .../com/example/grpc/EchoServiceClient.java | 126 ++ .../com/example/grpc/EchoServiceServer.java | 145 +++ .../com/example/grpc/GetCustomerRequest.java | 65 + .../com/example/grpc/GetCustomerResponse.java | 80 ++ .../com/example/grpc/Inner.java | 74 ++ .../com/example/grpc/Inventory.java | 159 +++ .../com/example/grpc/ListOrdersRequest.java | 74 ++ .../com/example/grpc/Notification.java | 102 ++ .../com/example/grpc/NotificationTarget.java | 23 + .../com/example/grpc/OptionalFields.java | 119 ++ .../com/example/grpc/Order.java | 123 ++ .../com/example/grpc/OrderId.java | 23 + .../com/example/grpc/OrderService.java | 16 + .../com/example/grpc/OrderServiceClient.java | 76 ++ .../com/example/grpc/OrderServiceServer.java | 95 ++ .../com/example/grpc/OrderStatus.java | 73 ++ .../com/example/grpc/OrderSummary.java | 74 ++ .../com/example/grpc/OrderUpdate.java | 114 ++ .../com/example/grpc/Outer.java | 89 ++ .../com/example/grpc/PaymentMethod.java | 132 ++ .../com/example/grpc/PaymentMethodMethod.java | 25 + .../com/example/grpc/Priority.java | 68 ++ .../com/example/grpc/ScalarTypes.java | 447 +++++++ .../com/example/grpc/Wallet.java | 74 ++ .../example/grpc/WellKnownTypesMessage.java | 226 ++++ .../com/example/grpc/GrpcIntegrationTest.java | 520 ++++++++ .../com/example/grpc/BankTransfer.java | 74 ++ .../com/example/grpc/ChatMessage.java | 114 ++ .../com/example/grpc/CreateOrderRequest.java | 80 ++ .../com/example/grpc/CreateOrderResponse.java | 74 ++ .../com/example/grpc/CreditCard.java | 83 ++ .../com/example/grpc/Customer.java | 83 ++ .../com/example/grpc/CustomerId.java | 23 + .../com/example/grpc/EchoService.java | 22 + .../com/example/grpc/EchoServiceClient.java | 126 ++ .../com/example/grpc/EchoServiceServer.java | 141 +++ .../com/example/grpc/GetCustomerRequest.java | 65 + .../com/example/grpc/GetCustomerResponse.java | 80 ++ .../com/example/grpc/Inner.java | 74 ++ .../com/example/grpc/Inventory.java | 159 +++ .../com/example/grpc/ListOrdersRequest.java | 74 ++ .../com/example/grpc/Notification.java | 102 ++ .../com/example/grpc/NotificationTarget.java | 23 + .../com/example/grpc/OptionalFields.java | 119 ++ .../com/example/grpc/Order.java | 123 ++ .../com/example/grpc/OrderId.java | 23 + .../com/example/grpc/OrderService.java | 16 + .../com/example/grpc/OrderServiceClient.java | 76 ++ .../com/example/grpc/OrderServiceServer.java | 91 ++ .../com/example/grpc/OrderStatus.java | 73 ++ .../com/example/grpc/OrderSummary.java | 74 ++ .../com/example/grpc/OrderUpdate.java | 114 ++ .../com/example/grpc/Outer.java | 89 ++ .../com/example/grpc/PaymentMethod.java | 132 ++ .../com/example/grpc/PaymentMethodMethod.java | 25 + .../com/example/grpc/Priority.java | 68 ++ .../com/example/grpc/ScalarTypes.java | 447 +++++++ .../com/example/grpc/Wallet.java | 74 ++ .../example/grpc/WellKnownTypesMessage.java | 226 ++++ .../com/example/grpc/GrpcIntegrationTest.java | 520 ++++++++ testers/grpc/kotlin-quarkus/build.gradle.kts | 40 + .../com/example/grpc/BankTransfer.kt | 65 + .../com/example/grpc/ChatMessage.kt | 85 ++ .../com/example/grpc/CreateOrderRequest.kt | 67 + .../com/example/grpc/CreateOrderResponse.kt | 65 + .../com/example/grpc/CreditCard.kt | 70 ++ .../com/example/grpc/Customer.kt | 70 ++ .../com/example/grpc/CustomerId.kt | 22 + .../com/example/grpc/EchoService.kt | 24 + .../com/example/grpc/EchoServiceClient.kt | 68 ++ .../com/example/grpc/EchoServiceServer.kt | 45 + .../com/example/grpc/GetCustomerRequest.kt | 58 + .../com/example/grpc/GetCustomerResponse.kt | 67 + .../com/example/grpc/Inner.kt | 65 + .../com/example/grpc/Inventory.kt | 110 ++ .../com/example/grpc/ListOrdersRequest.kt | 65 + .../com/example/grpc/Notification.kt | 85 ++ .../com/example/grpc/NotificationTarget.kt | 12 + .../com/example/grpc/OptionalFields.kt | 93 ++ .../com/example/grpc/Order.kt | 90 ++ .../com/example/grpc/OrderId.kt | 22 + .../com/example/grpc/OrderService.kt | 17 + .../com/example/grpc/OrderServiceClient.kt | 46 + .../com/example/grpc/OrderServiceServer.kt | 35 + .../com/example/grpc/OrderStatus.kt | 39 + .../com/example/grpc/OrderSummary.kt | 65 + .../com/example/grpc/OrderUpdate.kt | 85 ++ .../com/example/grpc/Outer.kt | 74 ++ .../com/example/grpc/PaymentMethod.kt | 104 ++ .../com/example/grpc/PaymentMethodMethod.kt | 12 + .../com/example/grpc/Priority.kt | 36 + .../com/example/grpc/ScalarTypes.kt | 131 ++ .../com/example/grpc/Wallet.kt | 65 + .../com/example/grpc/WellKnownTypesMessage.kt | 146 +++ testers/grpc/kotlin-quarkus/gradle.properties | 1 + .../com/example/grpc/GrpcIntegrationTest.kt | 448 +++++++ testers/grpc/kotlin/build.gradle.kts | 38 + .../com/example/grpc/BankTransfer.kt | 65 + .../com/example/grpc/ChatMessage.kt | 85 ++ .../com/example/grpc/CreateOrderRequest.kt | 67 + .../com/example/grpc/CreateOrderResponse.kt | 65 + .../com/example/grpc/CreditCard.kt | 70 ++ .../com/example/grpc/Customer.kt | 70 ++ .../com/example/grpc/CustomerId.kt | 22 + .../com/example/grpc/EchoService.kt | 24 + .../com/example/grpc/EchoServiceClient.kt | 66 + .../com/example/grpc/EchoServiceServer.kt | 43 + .../com/example/grpc/GetCustomerRequest.kt | 58 + .../com/example/grpc/GetCustomerResponse.kt | 67 + .../com/example/grpc/Inner.kt | 65 + .../com/example/grpc/Inventory.kt | 110 ++ .../com/example/grpc/ListOrdersRequest.kt | 65 + .../com/example/grpc/Notification.kt | 85 ++ .../com/example/grpc/NotificationTarget.kt | 12 + .../com/example/grpc/OptionalFields.kt | 93 ++ .../com/example/grpc/Order.kt | 90 ++ .../com/example/grpc/OrderId.kt | 22 + .../com/example/grpc/OrderService.kt | 16 + .../com/example/grpc/OrderServiceClient.kt | 44 + .../com/example/grpc/OrderServiceServer.kt | 33 + .../com/example/grpc/OrderStatus.kt | 39 + .../com/example/grpc/OrderSummary.kt | 65 + .../com/example/grpc/OrderUpdate.kt | 85 ++ .../com/example/grpc/Outer.kt | 74 ++ .../com/example/grpc/PaymentMethod.kt | 104 ++ .../com/example/grpc/PaymentMethodMethod.kt | 12 + .../com/example/grpc/Priority.kt | 36 + .../com/example/grpc/ScalarTypes.kt | 131 ++ .../com/example/grpc/Wallet.kt | 65 + .../com/example/grpc/WellKnownTypesMessage.kt | 146 +++ testers/grpc/kotlin/gradle.properties | 1 + .../com/example/grpc/GrpcIntegrationTest.kt | 449 +++++++ testers/grpc/protos/enums.proto | 22 + testers/grpc/protos/messages.proto | 78 ++ testers/grpc/protos/oneofs.proto | 46 + testers/grpc/protos/services.proto | 81 ++ testers/grpc/protos/typr/annotations.proto | 8 + .../com/example/grpc/BankTransfer.scala | 66 + .../com/example/grpc/ChatMessage.scala | 86 ++ .../com/example/grpc/CreateOrderRequest.scala | 68 ++ .../example/grpc/CreateOrderResponse.scala | 66 + .../com/example/grpc/CreditCard.scala | 71 ++ .../com/example/grpc/Customer.scala | 71 ++ .../com/example/grpc/CustomerId.scala | 18 + .../com/example/grpc/EchoService.scala | 24 + .../com/example/grpc/EchoServiceClient.scala | 66 + .../com/example/grpc/EchoServiceServer.scala | 34 + .../com/example/grpc/GetCustomerRequest.scala | 59 + .../example/grpc/GetCustomerResponse.scala | 68 ++ .../com/example/grpc/Inner.scala | 66 + .../com/example/grpc/Inventory.scala | 113 ++ .../com/example/grpc/ListOrdersRequest.scala | 66 + .../com/example/grpc/Notification.scala | 86 ++ .../com/example/grpc/NotificationTarget.scala | 14 + .../com/example/grpc/OptionalFields.scala | 94 ++ .../com/example/grpc/Order.scala | 96 ++ .../com/example/grpc/OrderId.scala | 18 + .../com/example/grpc/OrderService.scala | 16 + .../com/example/grpc/OrderServiceClient.scala | 43 + .../com/example/grpc/OrderServiceServer.scala | 29 + .../com/example/grpc/OrderStatus.scala | 36 + .../com/example/grpc/OrderSummary.scala | 66 + .../com/example/grpc/OrderUpdate.scala | 86 ++ .../com/example/grpc/Outer.scala | 75 ++ .../com/example/grpc/PaymentMethod.scala | 102 ++ .../example/grpc/PaymentMethodMethod.scala | 14 + .../com/example/grpc/Priority.scala | 34 + .../com/example/grpc/ScalarTypes.scala | 148 +++ .../com/example/grpc/Wallet.scala | 66 + .../example/grpc/WellKnownTypesMessage.scala | 153 +++ .../example/grpc/GrpcIntegrationTest.scala | 454 +++++++ .../src/scala/scripts/GenerateGrpcTest.scala | 146 +++ typr/src/resources/typr/annotations.proto | 8 + typr/src/scala/typr/Lang.scala | 16 + typr/src/scala/typr/Naming.scala | 87 ++ typr/src/scala/typr/TypeSupport.scala | 7 + typr/src/scala/typr/TypeSupportJava.scala | 15 + typr/src/scala/typr/TypeSupportScala.scala | 19 + .../typr/avro/codegen/RecordCodegen.scala | 1 + typr/src/scala/typr/effects/EffectType.scala | 36 + .../scala/typr/effects/EffectTypeOps.scala | 6 + .../typr/grpc/ComputedProtobufWrapper.scala | 64 + typr/src/scala/typr/grpc/GrpcCodegen.scala | 201 +++ typr/src/scala/typr/grpc/GrpcOptions.scala | 104 ++ typr/src/scala/typr/grpc/GrpcTypes.scala | 281 +++++ .../scala/typr/grpc/codegen/EnumCodegen.scala | 111 ++ .../typr/grpc/codegen/GrpcFramework.scala | 19 + .../grpc/codegen/GrpcFrameworkQuarkus.scala | 29 + .../grpc/codegen/GrpcFrameworkSpring.scala | 24 + .../typr/grpc/codegen/MessageCodegen.scala | 1079 +++++++++++++++++ .../typr/grpc/codegen/OneOfCodegen.scala | 65 + .../grpc/codegen/ProtobufTypeMapper.scala | 103 ++ .../typr/grpc/codegen/ServiceCodegen.scala | 505 ++++++++ .../typr/grpc/parser/GrpcParseError.scala | 36 + .../typr/grpc/parser/ProtobufParser.scala | 535 ++++++++ .../typr/internal/codegen/FileMariaSet.scala | 1 + .../internal/codegen/FileStringEnum.scala | 2 +- .../typr/internal/codegen/LangJava.scala | 7 +- .../typr/internal/codegen/LangKotlin.scala | 13 +- .../typr/internal/codegen/LangScala.scala | 14 +- .../internal/codegen/TypeSupportKotlin.scala | 16 + .../codegen/addPackageAndImports.scala | 22 +- typr/src/scala/typr/internal/minimize.scala | 7 +- typr/src/scala/typr/jvm.scala | 10 +- .../typr/openapi/codegen/ModelCodegen.scala | 1 + 249 files changed, 22725 insertions(+), 18 deletions(-) create mode 100644 testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/BankTransfer.java create mode 100644 testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/ChatMessage.java create mode 100644 testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/CreateOrderRequest.java create mode 100644 testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/CreateOrderResponse.java create mode 100644 testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/CreditCard.java create mode 100644 testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/Customer.java create mode 100644 testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/CustomerId.java create mode 100644 testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/EchoService.java create mode 100644 testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/EchoServiceClient.java create mode 100644 testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/EchoServiceServer.java create mode 100644 testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/GetCustomerRequest.java create mode 100644 testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/GetCustomerResponse.java create mode 100644 testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/Inner.java create mode 100644 testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/Inventory.java create mode 100644 testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/ListOrdersRequest.java create mode 100644 testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/Notification.java create mode 100644 testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/NotificationTarget.java create mode 100644 testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/OptionalFields.java create mode 100644 testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/Order.java create mode 100644 testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/OrderId.java create mode 100644 testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/OrderService.java create mode 100644 testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/OrderServiceClient.java create mode 100644 testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/OrderServiceServer.java create mode 100644 testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/OrderStatus.java create mode 100644 testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/OrderSummary.java create mode 100644 testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/OrderUpdate.java create mode 100644 testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/Outer.java create mode 100644 testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/PaymentMethod.java create mode 100644 testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/PaymentMethodMethod.java create mode 100644 testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/Priority.java create mode 100644 testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/ScalarTypes.java create mode 100644 testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/Wallet.java create mode 100644 testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/WellKnownTypesMessage.java create mode 100644 testers/grpc/java-quarkus/src/java/com/example/grpc/GrpcIntegrationTest.java create mode 100644 testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/BankTransfer.java create mode 100644 testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/ChatMessage.java create mode 100644 testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/CreateOrderRequest.java create mode 100644 testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/CreateOrderResponse.java create mode 100644 testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/CreditCard.java create mode 100644 testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/Customer.java create mode 100644 testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/CustomerId.java create mode 100644 testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/EchoService.java create mode 100644 testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/EchoServiceClient.java create mode 100644 testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/EchoServiceServer.java create mode 100644 testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/GetCustomerRequest.java create mode 100644 testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/GetCustomerResponse.java create mode 100644 testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/Inner.java create mode 100644 testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/Inventory.java create mode 100644 testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/ListOrdersRequest.java create mode 100644 testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/Notification.java create mode 100644 testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/NotificationTarget.java create mode 100644 testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/OptionalFields.java create mode 100644 testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/Order.java create mode 100644 testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/OrderId.java create mode 100644 testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/OrderService.java create mode 100644 testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/OrderServiceClient.java create mode 100644 testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/OrderServiceServer.java create mode 100644 testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/OrderStatus.java create mode 100644 testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/OrderSummary.java create mode 100644 testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/OrderUpdate.java create mode 100644 testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/Outer.java create mode 100644 testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/PaymentMethod.java create mode 100644 testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/PaymentMethodMethod.java create mode 100644 testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/Priority.java create mode 100644 testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/ScalarTypes.java create mode 100644 testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/Wallet.java create mode 100644 testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/WellKnownTypesMessage.java create mode 100644 testers/grpc/java-spring/src/java/com/example/grpc/GrpcIntegrationTest.java create mode 100644 testers/grpc/java/generated-and-checked-in/com/example/grpc/BankTransfer.java create mode 100644 testers/grpc/java/generated-and-checked-in/com/example/grpc/ChatMessage.java create mode 100644 testers/grpc/java/generated-and-checked-in/com/example/grpc/CreateOrderRequest.java create mode 100644 testers/grpc/java/generated-and-checked-in/com/example/grpc/CreateOrderResponse.java create mode 100644 testers/grpc/java/generated-and-checked-in/com/example/grpc/CreditCard.java create mode 100644 testers/grpc/java/generated-and-checked-in/com/example/grpc/Customer.java create mode 100644 testers/grpc/java/generated-and-checked-in/com/example/grpc/CustomerId.java create mode 100644 testers/grpc/java/generated-and-checked-in/com/example/grpc/EchoService.java create mode 100644 testers/grpc/java/generated-and-checked-in/com/example/grpc/EchoServiceClient.java create mode 100644 testers/grpc/java/generated-and-checked-in/com/example/grpc/EchoServiceServer.java create mode 100644 testers/grpc/java/generated-and-checked-in/com/example/grpc/GetCustomerRequest.java create mode 100644 testers/grpc/java/generated-and-checked-in/com/example/grpc/GetCustomerResponse.java create mode 100644 testers/grpc/java/generated-and-checked-in/com/example/grpc/Inner.java create mode 100644 testers/grpc/java/generated-and-checked-in/com/example/grpc/Inventory.java create mode 100644 testers/grpc/java/generated-and-checked-in/com/example/grpc/ListOrdersRequest.java create mode 100644 testers/grpc/java/generated-and-checked-in/com/example/grpc/Notification.java create mode 100644 testers/grpc/java/generated-and-checked-in/com/example/grpc/NotificationTarget.java create mode 100644 testers/grpc/java/generated-and-checked-in/com/example/grpc/OptionalFields.java create mode 100644 testers/grpc/java/generated-and-checked-in/com/example/grpc/Order.java create mode 100644 testers/grpc/java/generated-and-checked-in/com/example/grpc/OrderId.java create mode 100644 testers/grpc/java/generated-and-checked-in/com/example/grpc/OrderService.java create mode 100644 testers/grpc/java/generated-and-checked-in/com/example/grpc/OrderServiceClient.java create mode 100644 testers/grpc/java/generated-and-checked-in/com/example/grpc/OrderServiceServer.java create mode 100644 testers/grpc/java/generated-and-checked-in/com/example/grpc/OrderStatus.java create mode 100644 testers/grpc/java/generated-and-checked-in/com/example/grpc/OrderSummary.java create mode 100644 testers/grpc/java/generated-and-checked-in/com/example/grpc/OrderUpdate.java create mode 100644 testers/grpc/java/generated-and-checked-in/com/example/grpc/Outer.java create mode 100644 testers/grpc/java/generated-and-checked-in/com/example/grpc/PaymentMethod.java create mode 100644 testers/grpc/java/generated-and-checked-in/com/example/grpc/PaymentMethodMethod.java create mode 100644 testers/grpc/java/generated-and-checked-in/com/example/grpc/Priority.java create mode 100644 testers/grpc/java/generated-and-checked-in/com/example/grpc/ScalarTypes.java create mode 100644 testers/grpc/java/generated-and-checked-in/com/example/grpc/Wallet.java create mode 100644 testers/grpc/java/generated-and-checked-in/com/example/grpc/WellKnownTypesMessage.java create mode 100644 testers/grpc/java/src/java/com/example/grpc/GrpcIntegrationTest.java create mode 100644 testers/grpc/kotlin-quarkus/build.gradle.kts create mode 100644 testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/BankTransfer.kt create mode 100644 testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/ChatMessage.kt create mode 100644 testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/CreateOrderRequest.kt create mode 100644 testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/CreateOrderResponse.kt create mode 100644 testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/CreditCard.kt create mode 100644 testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/Customer.kt create mode 100644 testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/CustomerId.kt create mode 100644 testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/EchoService.kt create mode 100644 testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/EchoServiceClient.kt create mode 100644 testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/EchoServiceServer.kt create mode 100644 testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/GetCustomerRequest.kt create mode 100644 testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/GetCustomerResponse.kt create mode 100644 testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/Inner.kt create mode 100644 testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/Inventory.kt create mode 100644 testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/ListOrdersRequest.kt create mode 100644 testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/Notification.kt create mode 100644 testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/NotificationTarget.kt create mode 100644 testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/OptionalFields.kt create mode 100644 testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/Order.kt create mode 100644 testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/OrderId.kt create mode 100644 testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/OrderService.kt create mode 100644 testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/OrderServiceClient.kt create mode 100644 testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/OrderServiceServer.kt create mode 100644 testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/OrderStatus.kt create mode 100644 testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/OrderSummary.kt create mode 100644 testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/OrderUpdate.kt create mode 100644 testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/Outer.kt create mode 100644 testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/PaymentMethod.kt create mode 100644 testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/PaymentMethodMethod.kt create mode 100644 testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/Priority.kt create mode 100644 testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/ScalarTypes.kt create mode 100644 testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/Wallet.kt create mode 100644 testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/WellKnownTypesMessage.kt create mode 100644 testers/grpc/kotlin-quarkus/gradle.properties create mode 100644 testers/grpc/kotlin-quarkus/src/test/kotlin/com/example/grpc/GrpcIntegrationTest.kt create mode 100644 testers/grpc/kotlin/build.gradle.kts create mode 100644 testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/BankTransfer.kt create mode 100644 testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/ChatMessage.kt create mode 100644 testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/CreateOrderRequest.kt create mode 100644 testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/CreateOrderResponse.kt create mode 100644 testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/CreditCard.kt create mode 100644 testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/Customer.kt create mode 100644 testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/CustomerId.kt create mode 100644 testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/EchoService.kt create mode 100644 testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/EchoServiceClient.kt create mode 100644 testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/EchoServiceServer.kt create mode 100644 testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/GetCustomerRequest.kt create mode 100644 testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/GetCustomerResponse.kt create mode 100644 testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/Inner.kt create mode 100644 testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/Inventory.kt create mode 100644 testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/ListOrdersRequest.kt create mode 100644 testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/Notification.kt create mode 100644 testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/NotificationTarget.kt create mode 100644 testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/OptionalFields.kt create mode 100644 testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/Order.kt create mode 100644 testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/OrderId.kt create mode 100644 testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/OrderService.kt create mode 100644 testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/OrderServiceClient.kt create mode 100644 testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/OrderServiceServer.kt create mode 100644 testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/OrderStatus.kt create mode 100644 testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/OrderSummary.kt create mode 100644 testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/OrderUpdate.kt create mode 100644 testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/Outer.kt create mode 100644 testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/PaymentMethod.kt create mode 100644 testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/PaymentMethodMethod.kt create mode 100644 testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/Priority.kt create mode 100644 testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/ScalarTypes.kt create mode 100644 testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/Wallet.kt create mode 100644 testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/WellKnownTypesMessage.kt create mode 100644 testers/grpc/kotlin/gradle.properties create mode 100644 testers/grpc/kotlin/src/test/kotlin/com/example/grpc/GrpcIntegrationTest.kt create mode 100644 testers/grpc/protos/enums.proto create mode 100644 testers/grpc/protos/messages.proto create mode 100644 testers/grpc/protos/oneofs.proto create mode 100644 testers/grpc/protos/services.proto create mode 100644 testers/grpc/protos/typr/annotations.proto create mode 100644 testers/grpc/scala/generated-and-checked-in/com/example/grpc/BankTransfer.scala create mode 100644 testers/grpc/scala/generated-and-checked-in/com/example/grpc/ChatMessage.scala create mode 100644 testers/grpc/scala/generated-and-checked-in/com/example/grpc/CreateOrderRequest.scala create mode 100644 testers/grpc/scala/generated-and-checked-in/com/example/grpc/CreateOrderResponse.scala create mode 100644 testers/grpc/scala/generated-and-checked-in/com/example/grpc/CreditCard.scala create mode 100644 testers/grpc/scala/generated-and-checked-in/com/example/grpc/Customer.scala create mode 100644 testers/grpc/scala/generated-and-checked-in/com/example/grpc/CustomerId.scala create mode 100644 testers/grpc/scala/generated-and-checked-in/com/example/grpc/EchoService.scala create mode 100644 testers/grpc/scala/generated-and-checked-in/com/example/grpc/EchoServiceClient.scala create mode 100644 testers/grpc/scala/generated-and-checked-in/com/example/grpc/EchoServiceServer.scala create mode 100644 testers/grpc/scala/generated-and-checked-in/com/example/grpc/GetCustomerRequest.scala create mode 100644 testers/grpc/scala/generated-and-checked-in/com/example/grpc/GetCustomerResponse.scala create mode 100644 testers/grpc/scala/generated-and-checked-in/com/example/grpc/Inner.scala create mode 100644 testers/grpc/scala/generated-and-checked-in/com/example/grpc/Inventory.scala create mode 100644 testers/grpc/scala/generated-and-checked-in/com/example/grpc/ListOrdersRequest.scala create mode 100644 testers/grpc/scala/generated-and-checked-in/com/example/grpc/Notification.scala create mode 100644 testers/grpc/scala/generated-and-checked-in/com/example/grpc/NotificationTarget.scala create mode 100644 testers/grpc/scala/generated-and-checked-in/com/example/grpc/OptionalFields.scala create mode 100644 testers/grpc/scala/generated-and-checked-in/com/example/grpc/Order.scala create mode 100644 testers/grpc/scala/generated-and-checked-in/com/example/grpc/OrderId.scala create mode 100644 testers/grpc/scala/generated-and-checked-in/com/example/grpc/OrderService.scala create mode 100644 testers/grpc/scala/generated-and-checked-in/com/example/grpc/OrderServiceClient.scala create mode 100644 testers/grpc/scala/generated-and-checked-in/com/example/grpc/OrderServiceServer.scala create mode 100644 testers/grpc/scala/generated-and-checked-in/com/example/grpc/OrderStatus.scala create mode 100644 testers/grpc/scala/generated-and-checked-in/com/example/grpc/OrderSummary.scala create mode 100644 testers/grpc/scala/generated-and-checked-in/com/example/grpc/OrderUpdate.scala create mode 100644 testers/grpc/scala/generated-and-checked-in/com/example/grpc/Outer.scala create mode 100644 testers/grpc/scala/generated-and-checked-in/com/example/grpc/PaymentMethod.scala create mode 100644 testers/grpc/scala/generated-and-checked-in/com/example/grpc/PaymentMethodMethod.scala create mode 100644 testers/grpc/scala/generated-and-checked-in/com/example/grpc/Priority.scala create mode 100644 testers/grpc/scala/generated-and-checked-in/com/example/grpc/ScalarTypes.scala create mode 100644 testers/grpc/scala/generated-and-checked-in/com/example/grpc/Wallet.scala create mode 100644 testers/grpc/scala/generated-and-checked-in/com/example/grpc/WellKnownTypesMessage.scala create mode 100644 testers/grpc/scala/src/scala/com/example/grpc/GrpcIntegrationTest.scala create mode 100644 typr-scripts/src/scala/scripts/GenerateGrpcTest.scala create mode 100644 typr/src/resources/typr/annotations.proto create mode 100644 typr/src/scala/typr/grpc/ComputedProtobufWrapper.scala create mode 100644 typr/src/scala/typr/grpc/GrpcCodegen.scala create mode 100644 typr/src/scala/typr/grpc/GrpcOptions.scala create mode 100644 typr/src/scala/typr/grpc/GrpcTypes.scala create mode 100644 typr/src/scala/typr/grpc/codegen/EnumCodegen.scala create mode 100644 typr/src/scala/typr/grpc/codegen/GrpcFramework.scala create mode 100644 typr/src/scala/typr/grpc/codegen/GrpcFrameworkQuarkus.scala create mode 100644 typr/src/scala/typr/grpc/codegen/GrpcFrameworkSpring.scala create mode 100644 typr/src/scala/typr/grpc/codegen/MessageCodegen.scala create mode 100644 typr/src/scala/typr/grpc/codegen/OneOfCodegen.scala create mode 100644 typr/src/scala/typr/grpc/codegen/ProtobufTypeMapper.scala create mode 100644 typr/src/scala/typr/grpc/codegen/ServiceCodegen.scala create mode 100644 typr/src/scala/typr/grpc/parser/GrpcParseError.scala create mode 100644 typr/src/scala/typr/grpc/parser/ProtobufParser.scala diff --git a/bleep.yaml b/bleep.yaml index 922f3bf347..3ebe0e9518 100644 --- a/bleep.yaml +++ b/bleep.yaml @@ -657,6 +657,87 @@ projects: sources: - ./generated-and-checked-in - ./src + testers/grpc/java: + dependencies: + - com.google.protobuf:protobuf-java:4.29.3 + - com.novocode:junit-interface:0.11 + - io.grpc:grpc-netty-shaded:1.69.0 + - io.grpc:grpc-protobuf:1.69.0 + - io.grpc:grpc-stub:1.69.0 + - io.grpc:grpc-testing:1.69.0 + - io.grpc:grpc-inprocess:1.69.0 + - junit:junit:4.13.2 + dependsOn: foundations-jdbc + folder: ./testers/grpc/java + isTestProject: true + java: + options: -proc:none + platform: + name: jvm + sources: + - ./generated-and-checked-in + - ./src/java + testers/grpc/scala: + dependencies: + - com.google.protobuf:protobuf-java:4.29.3 + - com.novocode:junit-interface:0.11 + - io.grpc:grpc-netty-shaded:1.69.0 + - io.grpc:grpc-protobuf:1.69.0 + - io.grpc:grpc-stub:1.69.0 + - io.grpc:grpc-testing:1.69.0 + - io.grpc:grpc-inprocess:1.69.0 + - junit:junit:4.13.2 + dependsOn: foundations-jdbc + extends: template-scala-3 + isTestProject: true + sources: + - ./generated-and-checked-in + - ./src/scala + testers/grpc/java-spring: + dependencies: + - com.google.protobuf:protobuf-java:4.29.3 + - com.novocode:junit-interface:0.11 + - io.grpc:grpc-netty-shaded:1.69.0 + - io.grpc:grpc-protobuf:1.69.0 + - io.grpc:grpc-stub:1.69.0 + - io.grpc:grpc-testing:1.69.0 + - io.grpc:grpc-inprocess:1.69.0 + - junit:junit:4.13.2 + - org.springframework.grpc:spring-grpc-core:0.3.0 + - org.springframework:spring-context:6.2.1 + dependsOn: foundations-jdbc + folder: ./testers/grpc/java-spring + isTestProject: true + java: + options: -proc:none + platform: + name: jvm + sources: + - ./generated-and-checked-in + - ./src/java + testers/grpc/java-quarkus: + dependencies: + - com.google.protobuf:protobuf-java:4.29.3 + - com.novocode:junit-interface:0.11 + - io.grpc:grpc-netty-shaded:1.69.0 + - io.grpc:grpc-protobuf:1.69.0 + - io.grpc:grpc-stub:1.69.0 + - io.grpc:grpc-testing:1.69.0 + - io.grpc:grpc-inprocess:1.69.0 + - io.quarkus:quarkus-arc:3.17.2 + - io.quarkus:quarkus-grpc:3.17.2 + - jakarta.enterprise:jakarta.enterprise.cdi-api:4.1.0 + - junit:junit:4.13.2 + dependsOn: foundations-jdbc + folder: ./testers/grpc/java-quarkus + isTestProject: true + java: + options: -proc:none + platform: + name: jvm + sources: + - ./generated-and-checked-in + - ./src/java tests: dependencies: org.scalatest::scalatest:3.2.18 dependsOn: typr @@ -667,6 +748,9 @@ projects: - com.microsoft.sqlserver:mssql-jdbc:12.8.1.jre11 - com.oracle.database.jdbc:ojdbc11:23.6.0.24.10 - com.typesafe.play::play-json:2.10.6 + - com.google.protobuf:protobuf-java:4.29.3 + - io.grpc:grpc-protobuf:1.69.0 + - io.grpc:grpc-stub:1.69.0 - org.apache.avro:avro:1.12.0 - for3Use213: true module: io.get-coursier::coursier:2.1.24 @@ -738,6 +822,9 @@ scripts: generate-avro-test: main: scripts.GenerateAvroTest project: typr-scripts + generate-grpc-test: + main: scripts.GenerateGrpcTest + project: typr-scripts generate-db2: main: scripts.GeneratedDb2 project: typr-scripts diff --git a/settings.gradle.kts b/settings.gradle.kts index 1928cda50a..9715757391 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -36,6 +36,12 @@ project(":testers:avro:kotlin-json").projectDir = file("testers/avro/kotlin-json include("testers:avro:kotlin-quarkus-mutiny") project(":testers:avro:kotlin-quarkus-mutiny").projectDir = file("testers/avro/kotlin-quarkus-mutiny") +// gRPC Kotlin testers +include("testers:grpc:kotlin") +project(":testers:grpc:kotlin").projectDir = file("testers/grpc/kotlin") +include("testers:grpc:kotlin-quarkus") +project(":testers:grpc:kotlin-quarkus").projectDir = file("testers/grpc/kotlin-quarkus") + // OpenAPI Kotlin testers include("testers:openapi:kotlin:jaxrs") project(":testers:openapi:kotlin:jaxrs").projectDir = file("testers/openapi/kotlin/jaxrs") diff --git a/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/BankTransfer.java b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/BankTransfer.java new file mode 100644 index 0000000000..1fceb9c0ba --- /dev/null +++ b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/BankTransfer.java @@ -0,0 +1,74 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record BankTransfer(String accountNumber, String routingNumber) { + public BankTransfer withAccountNumber(String accountNumber) { + return new BankTransfer(accountNumber, routingNumber); + } + + public BankTransfer withRoutingNumber(String routingNumber) { + return new BankTransfer(accountNumber, routingNumber); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(BankTransfer value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public BankTransfer parse(InputStream stream) { + try { + return BankTransfer.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static BankTransfer parseFrom(CodedInputStream input) throws IOException { + String accountNumber = ""; + String routingNumber = ""; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + accountNumber = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + routingNumber = input.readString(); + } else { + input.skipField(tag); + } + ; + } + ; + return new BankTransfer(accountNumber, routingNumber); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeStringSize(1, this.accountNumber()); + size = size + CodedOutputStream.computeStringSize(2, this.routingNumber()); + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeString(1, this.accountNumber()); + output.writeString(2, this.routingNumber()); + } +} diff --git a/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/ChatMessage.java b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/ChatMessage.java new file mode 100644 index 0000000000..d144c468e5 --- /dev/null +++ b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/ChatMessage.java @@ -0,0 +1,114 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.time.Instant; + +public record ChatMessage(String sender, String content, Instant sentAt) { + public ChatMessage withSender(String sender) { + return new ChatMessage(sender, content, sentAt); + } + + public ChatMessage withContent(String content) { + return new ChatMessage(sender, content, sentAt); + } + + public ChatMessage withSentAt(Instant sentAt) { + return new ChatMessage(sender, content, sentAt); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(ChatMessage value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public ChatMessage parse(InputStream stream) { + try { + return ChatMessage.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static ChatMessage parseFrom(CodedInputStream input) throws IOException { + String sender = ""; + String content = ""; + Instant sentAt = Instant.EPOCH; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + sender = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + content = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 3) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + var _tsSeconds = 0L; + var _tsNanos = 0; + while (!input.isAtEnd()) { + var _tsTag = input.readTag(); + if (WireFormat.getTagFieldNumber(_tsTag) == 1) { + _tsSeconds = input.readInt64(); + } else if (WireFormat.getTagFieldNumber(_tsTag) == 2) { + _tsNanos = input.readInt32(); + } else { + input.skipField(_tsTag); + } + ; + } + ; + sentAt = Instant.ofEpochSecond(_tsSeconds, (long) (_tsNanos)); + input.popLimit(_oldLimit); + ; + } else { + input.skipField(tag); + } + ; + } + ; + return new ChatMessage(sender, content, sentAt); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeStringSize(1, this.sender()); + size = size + CodedOutputStream.computeStringSize(2, this.content()); + size = + size + + CodedOutputStream.computeTagSize(3) + + CodedOutputStream.computeUInt32SizeNoTag( + CodedOutputStream.computeInt64Size(1, this.sentAt().getEpochSecond()) + + CodedOutputStream.computeInt32Size(2, this.sentAt().getNano())) + + CodedOutputStream.computeInt64Size(1, this.sentAt().getEpochSecond()) + + CodedOutputStream.computeInt32Size(2, this.sentAt().getNano()); + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeString(1, this.sender()); + output.writeString(2, this.content()); + output.writeTag(3, 2); + output.writeUInt32NoTag( + CodedOutputStream.computeInt64Size(1, this.sentAt().getEpochSecond()) + + CodedOutputStream.computeInt32Size(2, this.sentAt().getNano())); + output.writeInt64(1, this.sentAt().getEpochSecond()); + output.writeInt32(2, this.sentAt().getNano()); + } +} diff --git a/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/CreateOrderRequest.java b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/CreateOrderRequest.java new file mode 100644 index 0000000000..2682d832d3 --- /dev/null +++ b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/CreateOrderRequest.java @@ -0,0 +1,80 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record CreateOrderRequest(Order order) { + public CreateOrderRequest withOrder(Order order) { + return new CreateOrderRequest(order); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(CreateOrderRequest value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public CreateOrderRequest parse(InputStream stream) { + try { + return CreateOrderRequest.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static CreateOrderRequest parseFrom(CodedInputStream input) throws IOException { + Order order = null; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + order = Order.parseFrom(input); + input.popLimit(_oldLimit); + ; + } else { + input.skipField(tag); + } + ; + } + ; + return new CreateOrderRequest(order); + } + + public Integer getSerializedSize() { + Integer size = 0; + if (!this.order().equals(null)) { + size = + size + + CodedOutputStream.computeTagSize(1) + + CodedOutputStream.computeUInt32SizeNoTag(this.order().getSerializedSize()) + + this.order().getSerializedSize(); + } + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + if (!this.order().equals(null)) { + output.writeTag(1, 2); + output.writeUInt32NoTag(this.order().getSerializedSize()); + this.order().writeTo(output); + ; + } + } +} diff --git a/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/CreateOrderResponse.java b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/CreateOrderResponse.java new file mode 100644 index 0000000000..405cc5b05f --- /dev/null +++ b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/CreateOrderResponse.java @@ -0,0 +1,74 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record CreateOrderResponse(String orderId, OrderStatus status) { + public CreateOrderResponse withOrderId(String orderId) { + return new CreateOrderResponse(orderId, status); + } + + public CreateOrderResponse withStatus(OrderStatus status) { + return new CreateOrderResponse(orderId, status); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(CreateOrderResponse value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public CreateOrderResponse parse(InputStream stream) { + try { + return CreateOrderResponse.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static CreateOrderResponse parseFrom(CodedInputStream input) throws IOException { + String orderId = ""; + OrderStatus status = OrderStatus.fromValue(0); + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + orderId = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + status = OrderStatus.fromValue(input.readEnum()); + } else { + input.skipField(tag); + } + ; + } + ; + return new CreateOrderResponse(orderId, status); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeStringSize(1, this.orderId()); + size = size + CodedOutputStream.computeEnumSize(2, this.status().toValue()); + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeString(1, this.orderId()); + output.writeEnum(2, this.status().toValue()); + } +} diff --git a/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/CreditCard.java b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/CreditCard.java new file mode 100644 index 0000000000..9ab67e6743 --- /dev/null +++ b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/CreditCard.java @@ -0,0 +1,83 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record CreditCard(String cardNumber, String expiryDate, String cvv) { + public CreditCard withCardNumber(String cardNumber) { + return new CreditCard(cardNumber, expiryDate, cvv); + } + + public CreditCard withExpiryDate(String expiryDate) { + return new CreditCard(cardNumber, expiryDate, cvv); + } + + public CreditCard withCvv(String cvv) { + return new CreditCard(cardNumber, expiryDate, cvv); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(CreditCard value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public CreditCard parse(InputStream stream) { + try { + return CreditCard.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static CreditCard parseFrom(CodedInputStream input) throws IOException { + String cardNumber = ""; + String expiryDate = ""; + String cvv = ""; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + cardNumber = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + expiryDate = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 3) { + cvv = input.readString(); + } else { + input.skipField(tag); + } + ; + } + ; + return new CreditCard(cardNumber, expiryDate, cvv); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeStringSize(1, this.cardNumber()); + size = size + CodedOutputStream.computeStringSize(2, this.expiryDate()); + size = size + CodedOutputStream.computeStringSize(3, this.cvv()); + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeString(1, this.cardNumber()); + output.writeString(2, this.expiryDate()); + output.writeString(3, this.cvv()); + } +} diff --git a/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/Customer.java b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/Customer.java new file mode 100644 index 0000000000..9b8f1e46aa --- /dev/null +++ b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/Customer.java @@ -0,0 +1,83 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record Customer(CustomerId customerId, String name, String email) { + public Customer withCustomerId(CustomerId customerId) { + return new Customer(customerId, name, email); + } + + public Customer withName(String name) { + return new Customer(customerId, name, email); + } + + public Customer withEmail(String email) { + return new Customer(customerId, name, email); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(Customer value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public Customer parse(InputStream stream) { + try { + return Customer.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static Customer parseFrom(CodedInputStream input) throws IOException { + CustomerId customerId = CustomerId.valueOf(""); + String name = ""; + String email = ""; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + customerId = CustomerId.valueOf(input.readString()); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + name = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 3) { + email = input.readString(); + } else { + input.skipField(tag); + } + ; + } + ; + return new Customer(customerId, name, email); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeStringSize(1, this.customerId().unwrap()); + size = size + CodedOutputStream.computeStringSize(2, this.name()); + size = size + CodedOutputStream.computeStringSize(3, this.email()); + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeString(1, this.customerId().unwrap()); + output.writeString(2, this.name()); + output.writeString(3, this.email()); + } +} diff --git a/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/CustomerId.java b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/CustomerId.java new file mode 100644 index 0000000000..ded4db1d1e --- /dev/null +++ b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/CustomerId.java @@ -0,0 +1,23 @@ +package com.example.grpc; + +/** Wrapper type for typr.grpc.GrpcCodegen$$$Lambda/0x00007fc001118400@38145825 */ +public record CustomerId(String value) { + public CustomerId withValue(String value) { + return new CustomerId(value); + } + + @Override + public java.lang.String toString() { + return value.toString(); + } + + /** Create a CustomerId from a raw value */ + public static CustomerId valueOf(String v) { + return new CustomerId(v); + } + + /** Get the underlying value */ + public String unwrap() { + return this.value(); + } +} diff --git a/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/EchoService.java b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/EchoService.java new file mode 100644 index 0000000000..c88b0ece5c --- /dev/null +++ b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/EchoService.java @@ -0,0 +1,22 @@ +package com.example.grpc; + +/** Clean service interface for EchoService gRPC service */ +public interface EchoService { + ScalarTypes echoScalarTypes(ScalarTypes request); + + Customer echoCustomer(Customer request); + + Order echoOrder(Order request); + + Inventory echoInventory(Inventory request); + + Outer echoOuter(Outer request); + + OptionalFields echoOptionalFields(OptionalFields request); + + WellKnownTypesMessage echoWellKnownTypes(WellKnownTypesMessage request); + + PaymentMethod echoPaymentMethod(PaymentMethod request); + + Notification echoNotification(Notification request); +} diff --git a/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/EchoServiceClient.java b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/EchoServiceClient.java new file mode 100644 index 0000000000..73bfb2aaac --- /dev/null +++ b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/EchoServiceClient.java @@ -0,0 +1,127 @@ +package com.example.grpc; + +import io.grpc.CallOptions; +import io.grpc.Channel; +import io.grpc.MethodDescriptor; +import io.grpc.MethodDescriptor.MethodType; +import io.grpc.stub.ClientCalls; +import io.quarkus.grpc.GrpcClient; + +/** gRPC client wrapper for EchoService - wraps Channel with clean types */ +public class EchoServiceClient implements EchoService { + Channel channel; + + public EchoServiceClient(@GrpcClient("EchoService") Channel channel) { + this.channel = channel; + } + + public static MethodDescriptor ECHO_CUSTOMER = + MethodDescriptor.newBuilder(Customer.MARSHALLER, Customer.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoCustomer") + .build(); + + public static MethodDescriptor ECHO_INVENTORY = + MethodDescriptor.newBuilder(Inventory.MARSHALLER, Inventory.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoInventory") + .build(); + + public static MethodDescriptor ECHO_NOTIFICATION = + MethodDescriptor.newBuilder(Notification.MARSHALLER, Notification.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoNotification") + .build(); + + public static MethodDescriptor ECHO_OPTIONAL_FIELDS = + MethodDescriptor.newBuilder(OptionalFields.MARSHALLER, OptionalFields.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoOptionalFields") + .build(); + + public static MethodDescriptor ECHO_ORDER = + MethodDescriptor.newBuilder(Order.MARSHALLER, Order.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoOrder") + .build(); + + public static MethodDescriptor ECHO_OUTER = + MethodDescriptor.newBuilder(Outer.MARSHALLER, Outer.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoOuter") + .build(); + + public static MethodDescriptor ECHO_PAYMENT_METHOD = + MethodDescriptor.newBuilder(PaymentMethod.MARSHALLER, PaymentMethod.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoPaymentMethod") + .build(); + + public static MethodDescriptor ECHO_SCALAR_TYPES = + MethodDescriptor.newBuilder(ScalarTypes.MARSHALLER, ScalarTypes.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoScalarTypes") + .build(); + + public static MethodDescriptor + ECHO_WELL_KNOWN_TYPES = + MethodDescriptor.newBuilder( + WellKnownTypesMessage.MARSHALLER, WellKnownTypesMessage.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoWellKnownTypes") + .build(); + + @Override + public ScalarTypes echoScalarTypes(ScalarTypes request) { + return ClientCalls.blockingUnaryCall( + channel, EchoServiceClient.ECHO_SCALAR_TYPES, CallOptions.DEFAULT, request); + } + + @Override + public Customer echoCustomer(Customer request) { + return ClientCalls.blockingUnaryCall( + channel, EchoServiceClient.ECHO_CUSTOMER, CallOptions.DEFAULT, request); + } + + @Override + public Order echoOrder(Order request) { + return ClientCalls.blockingUnaryCall( + channel, EchoServiceClient.ECHO_ORDER, CallOptions.DEFAULT, request); + } + + @Override + public Inventory echoInventory(Inventory request) { + return ClientCalls.blockingUnaryCall( + channel, EchoServiceClient.ECHO_INVENTORY, CallOptions.DEFAULT, request); + } + + @Override + public Outer echoOuter(Outer request) { + return ClientCalls.blockingUnaryCall( + channel, EchoServiceClient.ECHO_OUTER, CallOptions.DEFAULT, request); + } + + @Override + public OptionalFields echoOptionalFields(OptionalFields request) { + return ClientCalls.blockingUnaryCall( + channel, EchoServiceClient.ECHO_OPTIONAL_FIELDS, CallOptions.DEFAULT, request); + } + + @Override + public WellKnownTypesMessage echoWellKnownTypes(WellKnownTypesMessage request) { + return ClientCalls.blockingUnaryCall( + channel, EchoServiceClient.ECHO_WELL_KNOWN_TYPES, CallOptions.DEFAULT, request); + } + + @Override + public PaymentMethod echoPaymentMethod(PaymentMethod request) { + return ClientCalls.blockingUnaryCall( + channel, EchoServiceClient.ECHO_PAYMENT_METHOD, CallOptions.DEFAULT, request); + } + + @Override + public Notification echoNotification(Notification request) { + return ClientCalls.blockingUnaryCall( + channel, EchoServiceClient.ECHO_NOTIFICATION, CallOptions.DEFAULT, request); + } +} diff --git a/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/EchoServiceServer.java b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/EchoServiceServer.java new file mode 100644 index 0000000000..ea5f9fd14b --- /dev/null +++ b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/EchoServiceServer.java @@ -0,0 +1,145 @@ +package com.example.grpc; + +import io.grpc.BindableService; +import io.grpc.MethodDescriptor; +import io.grpc.MethodDescriptor.MethodType; +import io.grpc.ServerServiceDefinition; +import io.grpc.stub.ServerCalls; +import io.quarkus.grpc.GrpcService; +import jakarta.inject.Singleton; + +/** gRPC server adapter for EchoService - delegates to clean service interface */ +@GrpcService +@Singleton +public class EchoServiceServer implements BindableService { + EchoService delegate; + + public EchoServiceServer(EchoService delegate) { + this.delegate = delegate; + } + + public static MethodDescriptor ECHO_CUSTOMER = + MethodDescriptor.newBuilder(Customer.MARSHALLER, Customer.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoCustomer") + .build(); + + public static MethodDescriptor ECHO_INVENTORY = + MethodDescriptor.newBuilder(Inventory.MARSHALLER, Inventory.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoInventory") + .build(); + + public static MethodDescriptor ECHO_NOTIFICATION = + MethodDescriptor.newBuilder(Notification.MARSHALLER, Notification.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoNotification") + .build(); + + public static MethodDescriptor ECHO_OPTIONAL_FIELDS = + MethodDescriptor.newBuilder(OptionalFields.MARSHALLER, OptionalFields.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoOptionalFields") + .build(); + + public static MethodDescriptor ECHO_ORDER = + MethodDescriptor.newBuilder(Order.MARSHALLER, Order.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoOrder") + .build(); + + public static MethodDescriptor ECHO_OUTER = + MethodDescriptor.newBuilder(Outer.MARSHALLER, Outer.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoOuter") + .build(); + + public static MethodDescriptor ECHO_PAYMENT_METHOD = + MethodDescriptor.newBuilder(PaymentMethod.MARSHALLER, PaymentMethod.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoPaymentMethod") + .build(); + + public static MethodDescriptor ECHO_SCALAR_TYPES = + MethodDescriptor.newBuilder(ScalarTypes.MARSHALLER, ScalarTypes.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoScalarTypes") + .build(); + + public static MethodDescriptor + ECHO_WELL_KNOWN_TYPES = + MethodDescriptor.newBuilder( + WellKnownTypesMessage.MARSHALLER, WellKnownTypesMessage.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoWellKnownTypes") + .build(); + + @Override + public ServerServiceDefinition bindService() { + return ServerServiceDefinition.builder("testgrpc.EchoService") + .addMethod( + EchoServiceServer.ECHO_SCALAR_TYPES, + ServerCalls.asyncUnaryCall( + (request, responseObserver) -> { + responseObserver.onNext(delegate.echoScalarTypes(request)); + responseObserver.onCompleted(); + })) + .addMethod( + EchoServiceServer.ECHO_CUSTOMER, + ServerCalls.asyncUnaryCall( + (request, responseObserver) -> { + responseObserver.onNext(delegate.echoCustomer(request)); + responseObserver.onCompleted(); + })) + .addMethod( + EchoServiceServer.ECHO_ORDER, + ServerCalls.asyncUnaryCall( + (request, responseObserver) -> { + responseObserver.onNext(delegate.echoOrder(request)); + responseObserver.onCompleted(); + })) + .addMethod( + EchoServiceServer.ECHO_INVENTORY, + ServerCalls.asyncUnaryCall( + (request, responseObserver) -> { + responseObserver.onNext(delegate.echoInventory(request)); + responseObserver.onCompleted(); + })) + .addMethod( + EchoServiceServer.ECHO_OUTER, + ServerCalls.asyncUnaryCall( + (request, responseObserver) -> { + responseObserver.onNext(delegate.echoOuter(request)); + responseObserver.onCompleted(); + })) + .addMethod( + EchoServiceServer.ECHO_OPTIONAL_FIELDS, + ServerCalls.asyncUnaryCall( + (request, responseObserver) -> { + responseObserver.onNext(delegate.echoOptionalFields(request)); + responseObserver.onCompleted(); + })) + .addMethod( + EchoServiceServer.ECHO_WELL_KNOWN_TYPES, + ServerCalls.asyncUnaryCall( + (request, responseObserver) -> { + responseObserver.onNext(delegate.echoWellKnownTypes(request)); + responseObserver.onCompleted(); + })) + .addMethod( + EchoServiceServer.ECHO_PAYMENT_METHOD, + ServerCalls.asyncUnaryCall( + (request, responseObserver) -> { + responseObserver.onNext(delegate.echoPaymentMethod(request)); + responseObserver.onCompleted(); + })) + .addMethod( + EchoServiceServer.ECHO_NOTIFICATION, + ServerCalls.asyncUnaryCall( + (request, responseObserver) -> { + responseObserver.onNext(delegate.echoNotification(request)); + responseObserver.onCompleted(); + })) + .build(); + } +} diff --git a/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/GetCustomerRequest.java b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/GetCustomerRequest.java new file mode 100644 index 0000000000..5d30c0ed46 --- /dev/null +++ b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/GetCustomerRequest.java @@ -0,0 +1,65 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record GetCustomerRequest(String customerId) { + public GetCustomerRequest withCustomerId(String customerId) { + return new GetCustomerRequest(customerId); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(GetCustomerRequest value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public GetCustomerRequest parse(InputStream stream) { + try { + return GetCustomerRequest.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static GetCustomerRequest parseFrom(CodedInputStream input) throws IOException { + String customerId = ""; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + customerId = input.readString(); + } else { + input.skipField(tag); + } + ; + } + ; + return new GetCustomerRequest(customerId); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeStringSize(1, this.customerId()); + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeString(1, this.customerId()); + } +} diff --git a/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/GetCustomerResponse.java b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/GetCustomerResponse.java new file mode 100644 index 0000000000..3a21596211 --- /dev/null +++ b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/GetCustomerResponse.java @@ -0,0 +1,80 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record GetCustomerResponse(Customer customer) { + public GetCustomerResponse withCustomer(Customer customer) { + return new GetCustomerResponse(customer); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(GetCustomerResponse value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public GetCustomerResponse parse(InputStream stream) { + try { + return GetCustomerResponse.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static GetCustomerResponse parseFrom(CodedInputStream input) throws IOException { + Customer customer = null; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + customer = Customer.parseFrom(input); + input.popLimit(_oldLimit); + ; + } else { + input.skipField(tag); + } + ; + } + ; + return new GetCustomerResponse(customer); + } + + public Integer getSerializedSize() { + Integer size = 0; + if (!this.customer().equals(null)) { + size = + size + + CodedOutputStream.computeTagSize(1) + + CodedOutputStream.computeUInt32SizeNoTag(this.customer().getSerializedSize()) + + this.customer().getSerializedSize(); + } + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + if (!this.customer().equals(null)) { + output.writeTag(1, 2); + output.writeUInt32NoTag(this.customer().getSerializedSize()); + this.customer().writeTo(output); + ; + } + } +} diff --git a/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/Inner.java b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/Inner.java new file mode 100644 index 0000000000..4b45f35f6d --- /dev/null +++ b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/Inner.java @@ -0,0 +1,74 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record Inner(Integer value, String description) { + public Inner withValue(Integer value) { + return new Inner(value, description); + } + + public Inner withDescription(String description) { + return new Inner(value, description); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(Inner value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public Inner parse(InputStream stream) { + try { + return Inner.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static Inner parseFrom(CodedInputStream input) throws IOException { + Integer value = 0; + String description = ""; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + value = input.readInt32(); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + description = input.readString(); + } else { + input.skipField(tag); + } + ; + } + ; + return new Inner(value, description); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeInt32Size(1, this.value()); + size = size + CodedOutputStream.computeStringSize(2, this.description()); + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeInt32(1, this.value()); + output.writeString(2, this.description()); + } +} diff --git a/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/Inventory.java b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/Inventory.java new file mode 100644 index 0000000000..0e8b63bcc4 --- /dev/null +++ b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/Inventory.java @@ -0,0 +1,159 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +public record Inventory( + String warehouseId, + List productIds, + Map stockCounts, + List recentOrders) { + public Inventory withWarehouseId(String warehouseId) { + return new Inventory(warehouseId, productIds, stockCounts, recentOrders); + } + + public Inventory withProductIds(List productIds) { + return new Inventory(warehouseId, productIds, stockCounts, recentOrders); + } + + public Inventory withStockCounts(Map stockCounts) { + return new Inventory(warehouseId, productIds, stockCounts, recentOrders); + } + + public Inventory withRecentOrders(List recentOrders) { + return new Inventory(warehouseId, productIds, stockCounts, recentOrders); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(Inventory value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public Inventory parse(InputStream stream) { + try { + return Inventory.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static Inventory parseFrom(CodedInputStream input) throws IOException { + String warehouseId = ""; + ArrayList productIds = new ArrayList<>(); + HashMap stockCounts = new HashMap(); + ArrayList recentOrders = new ArrayList<>(); + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + warehouseId = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + productIds.add(input.readString()); + } else if (WireFormat.getTagFieldNumber(tag) == 3) { + var length = input.readRawVarint32(); + var oldLimit = input.pushLimit(length); + var mapKey = ""; + var mapValue = 0; + while (!input.isAtEnd()) { + var entryTag = input.readTag(); + if (WireFormat.getTagFieldNumber(entryTag) == 1) { + mapKey = input.readString(); + } else if (WireFormat.getTagFieldNumber(entryTag) == 2) { + mapValue = input.readInt32(); + } else { + input.skipField(entryTag); + } + ; + } + ; + input.popLimit(oldLimit); + stockCounts.put(mapKey, mapValue); + ; + } else if (WireFormat.getTagFieldNumber(tag) == 4) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + recentOrders.add(Order.parseFrom(input)); + input.popLimit(_oldLimit); + ; + } else { + input.skipField(tag); + } + ; + } + ; + return new Inventory(warehouseId, productIds, stockCounts, recentOrders); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeStringSize(1, this.warehouseId()); + for (String elem : this.productIds()) { + size = size + CodedOutputStream.computeStringSize(2, elem); + } + ; + for (Entry entry : this.stockCounts().entrySet()) { + size = + size + + CodedOutputStream.computeTagSize(3) + + CodedOutputStream.computeUInt32SizeNoTag( + CodedOutputStream.computeStringSize(1, entry.getKey()) + + CodedOutputStream.computeInt32Size(2, entry.getValue())) + + CodedOutputStream.computeStringSize(1, entry.getKey()) + + CodedOutputStream.computeInt32Size(2, entry.getValue()); + } + ; + for (Order elem : this.recentOrders()) { + size = + size + + CodedOutputStream.computeTagSize(4) + + CodedOutputStream.computeUInt32SizeNoTag(elem.getSerializedSize()) + + elem.getSerializedSize(); + } + ; + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeString(1, this.warehouseId()); + for (String elem : this.productIds()) { + output.writeString(2, elem); + } + ; + for (Entry entry : this.stockCounts().entrySet()) { + output.writeTag(3, 2); + output.writeUInt32NoTag( + CodedOutputStream.computeStringSize(1, entry.getKey()) + + CodedOutputStream.computeInt32Size(2, entry.getValue())); + output.writeString(1, entry.getKey()); + output.writeInt32(2, entry.getValue()); + } + ; + for (Order elem : this.recentOrders()) { + output.writeTag(4, 2); + output.writeUInt32NoTag(elem.getSerializedSize()); + elem.writeTo(output); + } + ; + } +} diff --git a/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/ListOrdersRequest.java b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/ListOrdersRequest.java new file mode 100644 index 0000000000..3a12489bec --- /dev/null +++ b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/ListOrdersRequest.java @@ -0,0 +1,74 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record ListOrdersRequest(String customerId, Integer pageSize) { + public ListOrdersRequest withCustomerId(String customerId) { + return new ListOrdersRequest(customerId, pageSize); + } + + public ListOrdersRequest withPageSize(Integer pageSize) { + return new ListOrdersRequest(customerId, pageSize); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(ListOrdersRequest value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public ListOrdersRequest parse(InputStream stream) { + try { + return ListOrdersRequest.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static ListOrdersRequest parseFrom(CodedInputStream input) throws IOException { + String customerId = ""; + Integer pageSize = 0; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + customerId = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + pageSize = input.readInt32(); + } else { + input.skipField(tag); + } + ; + } + ; + return new ListOrdersRequest(customerId, pageSize); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeStringSize(1, this.customerId()); + size = size + CodedOutputStream.computeInt32Size(2, this.pageSize()); + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeString(1, this.customerId()); + output.writeInt32(2, this.pageSize()); + } +} diff --git a/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/Notification.java b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/Notification.java new file mode 100644 index 0000000000..908f7b7844 --- /dev/null +++ b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/Notification.java @@ -0,0 +1,102 @@ +package com.example.grpc; + +import com.example.grpc.NotificationTarget.Email; +import com.example.grpc.NotificationTarget.Phone; +import com.example.grpc.NotificationTarget.WebhookUrl; +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record Notification(String message, Priority priority, NotificationTarget target) { + public Notification withMessage(String message) { + return new Notification(message, priority, target); + } + + public Notification withPriority(Priority priority) { + return new Notification(message, priority, target); + } + + public Notification withTarget(NotificationTarget target) { + return new Notification(message, priority, target); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(Notification value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public Notification parse(InputStream stream) { + try { + return Notification.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static Notification parseFrom(CodedInputStream input) throws IOException { + String message = ""; + Priority priority = Priority.fromValue(0); + NotificationTarget target = null; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + message = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + priority = Priority.fromValue(input.readEnum()); + } else if (WireFormat.getTagFieldNumber(tag) == 3) { + target = new Email(input.readString()); + } else if (WireFormat.getTagFieldNumber(tag) == 4) { + target = new Phone(input.readString()); + } else if (WireFormat.getTagFieldNumber(tag) == 5) { + target = new WebhookUrl(input.readString()); + } else { + input.skipField(tag); + } + ; + } + ; + return new Notification(message, priority, target); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeStringSize(1, this.message()); + size = size + CodedOutputStream.computeEnumSize(2, this.priority().toValue()); + switch (this.target()) { + case null -> {} + case Email c -> size = size + CodedOutputStream.computeStringSize(3, c.email()); + case Phone c -> size = size + CodedOutputStream.computeStringSize(4, c.phone()); + case WebhookUrl c -> size = size + CodedOutputStream.computeStringSize(5, c.webhookUrl()); + } + ; + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeString(1, this.message()); + output.writeEnum(2, this.priority().toValue()); + switch (this.target()) { + case null -> {} + case Email c -> output.writeString(3, c.email()); + case Phone c -> output.writeString(4, c.phone()); + case WebhookUrl c -> output.writeString(5, c.webhookUrl()); + } + ; + } +} diff --git a/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/NotificationTarget.java b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/NotificationTarget.java new file mode 100644 index 0000000000..f95bbe8b48 --- /dev/null +++ b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/NotificationTarget.java @@ -0,0 +1,23 @@ +package com.example.grpc; + +/** OneOf type for target */ +public sealed interface NotificationTarget + permits NotificationTarget.Email, NotificationTarget.Phone, NotificationTarget.WebhookUrl { + record Email(String email) implements NotificationTarget { + public Email withEmail(String email) { + return new Email(email); + } + } + + record Phone(String phone) implements NotificationTarget { + public Phone withPhone(String phone) { + return new Phone(phone); + } + } + + record WebhookUrl(String webhookUrl) implements NotificationTarget { + public WebhookUrl withWebhookUrl(String webhookUrl) { + return new WebhookUrl(webhookUrl); + } + } +} diff --git a/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/OptionalFields.java b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/OptionalFields.java new file mode 100644 index 0000000000..534c31a211 --- /dev/null +++ b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/OptionalFields.java @@ -0,0 +1,119 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Optional; + +public record OptionalFields( + Optional name, Optional age, Optional customer) { + public OptionalFields withName(Optional name) { + return new OptionalFields(name, age, customer); + } + + public OptionalFields withAge(Optional age) { + return new OptionalFields(name, age, customer); + } + + public OptionalFields withCustomer(Optional customer) { + return new OptionalFields(name, age, customer); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(OptionalFields value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public OptionalFields parse(InputStream stream) { + try { + return OptionalFields.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static OptionalFields parseFrom(CodedInputStream input) throws IOException { + Optional name = Optional.empty(); + Optional age = Optional.empty(); + Optional customer = Optional.empty(); + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + name = Optional.of(input.readString()); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + age = Optional.of(input.readInt32()); + } else if (WireFormat.getTagFieldNumber(tag) == 3) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + customer = Optional.of(Customer.parseFrom(input)); + input.popLimit(_oldLimit); + ; + } else { + input.skipField(tag); + } + ; + } + ; + return new OptionalFields(name, age, customer); + } + + public Integer getSerializedSize() { + Integer size = 0; + if (this.name().isPresent()) { + var v = this.name().get(); + size = size + CodedOutputStream.computeStringSize(1, v); + ; + } + if (this.age().isPresent()) { + var v = this.age().get(); + size = size + CodedOutputStream.computeInt32Size(2, v); + ; + } + if (this.customer().isPresent()) { + var v = this.customer().get(); + size = + size + + CodedOutputStream.computeTagSize(3) + + CodedOutputStream.computeUInt32SizeNoTag(v.getSerializedSize()) + + v.getSerializedSize(); + ; + } + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + if (this.name().isPresent()) { + var v = this.name().get(); + output.writeString(1, v); + ; + } + if (this.age().isPresent()) { + var v = this.age().get(); + output.writeInt32(2, v); + ; + } + if (this.customer().isPresent()) { + var v = this.customer().get(); + output.writeTag(3, 2); + output.writeUInt32NoTag(v.getSerializedSize()); + v.writeTo(output); + ; + } + } +} diff --git a/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/Order.java b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/Order.java new file mode 100644 index 0000000000..a1dd05fa0b --- /dev/null +++ b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/Order.java @@ -0,0 +1,123 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.time.Instant; + +public record Order(OrderId orderId, CustomerId customerId, Long amountCents, Instant createdAt) { + public Order withOrderId(OrderId orderId) { + return new Order(orderId, customerId, amountCents, createdAt); + } + + public Order withCustomerId(CustomerId customerId) { + return new Order(orderId, customerId, amountCents, createdAt); + } + + public Order withAmountCents(Long amountCents) { + return new Order(orderId, customerId, amountCents, createdAt); + } + + public Order withCreatedAt(Instant createdAt) { + return new Order(orderId, customerId, amountCents, createdAt); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(Order value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public Order parse(InputStream stream) { + try { + return Order.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static Order parseFrom(CodedInputStream input) throws IOException { + OrderId orderId = OrderId.valueOf(""); + CustomerId customerId = CustomerId.valueOf(""); + Long amountCents = 0L; + Instant createdAt = Instant.EPOCH; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + orderId = OrderId.valueOf(input.readString()); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + customerId = CustomerId.valueOf(input.readString()); + } else if (WireFormat.getTagFieldNumber(tag) == 3) { + amountCents = input.readInt64(); + } else if (WireFormat.getTagFieldNumber(tag) == 4) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + var _tsSeconds = 0L; + var _tsNanos = 0; + while (!input.isAtEnd()) { + var _tsTag = input.readTag(); + if (WireFormat.getTagFieldNumber(_tsTag) == 1) { + _tsSeconds = input.readInt64(); + } else if (WireFormat.getTagFieldNumber(_tsTag) == 2) { + _tsNanos = input.readInt32(); + } else { + input.skipField(_tsTag); + } + ; + } + ; + createdAt = Instant.ofEpochSecond(_tsSeconds, (long) (_tsNanos)); + input.popLimit(_oldLimit); + ; + } else { + input.skipField(tag); + } + ; + } + ; + return new Order(orderId, customerId, amountCents, createdAt); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeStringSize(1, this.orderId().unwrap()); + size = size + CodedOutputStream.computeStringSize(2, this.customerId().unwrap()); + size = size + CodedOutputStream.computeInt64Size(3, this.amountCents()); + size = + size + + CodedOutputStream.computeTagSize(4) + + CodedOutputStream.computeUInt32SizeNoTag( + CodedOutputStream.computeInt64Size(1, this.createdAt().getEpochSecond()) + + CodedOutputStream.computeInt32Size(2, this.createdAt().getNano())) + + CodedOutputStream.computeInt64Size(1, this.createdAt().getEpochSecond()) + + CodedOutputStream.computeInt32Size(2, this.createdAt().getNano()); + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeString(1, this.orderId().unwrap()); + output.writeString(2, this.customerId().unwrap()); + output.writeInt64(3, this.amountCents()); + output.writeTag(4, 2); + output.writeUInt32NoTag( + CodedOutputStream.computeInt64Size(1, this.createdAt().getEpochSecond()) + + CodedOutputStream.computeInt32Size(2, this.createdAt().getNano())); + output.writeInt64(1, this.createdAt().getEpochSecond()); + output.writeInt32(2, this.createdAt().getNano()); + } +} diff --git a/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/OrderId.java b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/OrderId.java new file mode 100644 index 0000000000..0b7f7a1e40 --- /dev/null +++ b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/OrderId.java @@ -0,0 +1,23 @@ +package com.example.grpc; + +/** Wrapper type for typr.grpc.GrpcCodegen$$$Lambda/0x00007fc001118400@41330d4f */ +public record OrderId(String value) { + public OrderId withValue(String value) { + return new OrderId(value); + } + + @Override + public java.lang.String toString() { + return value.toString(); + } + + /** Create a OrderId from a raw value */ + public static OrderId valueOf(String v) { + return new OrderId(v); + } + + /** Get the underlying value */ + public String unwrap() { + return this.value(); + } +} diff --git a/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/OrderService.java b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/OrderService.java new file mode 100644 index 0000000000..743491970c --- /dev/null +++ b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/OrderService.java @@ -0,0 +1,16 @@ +package com.example.grpc; + +import java.util.Iterator; + +/** Clean service interface for OrderService gRPC service */ +public interface OrderService { + GetCustomerResponse getCustomer(GetCustomerRequest request); + + CreateOrderResponse createOrder(CreateOrderRequest request); + + Iterator listOrders(ListOrdersRequest request); + + OrderSummary submitOrders(Iterator requests); + + Iterator chat(Iterator requests); +} diff --git a/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/OrderServiceClient.java b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/OrderServiceClient.java new file mode 100644 index 0000000000..d6b33bd4f6 --- /dev/null +++ b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/OrderServiceClient.java @@ -0,0 +1,77 @@ +package com.example.grpc; + +import io.grpc.CallOptions; +import io.grpc.Channel; +import io.grpc.MethodDescriptor; +import io.grpc.MethodDescriptor.MethodType; +import io.grpc.stub.ClientCalls; +import io.quarkus.grpc.GrpcClient; +import java.util.Iterator; + +/** gRPC client wrapper for OrderService - wraps Channel with clean types */ +public class OrderServiceClient implements OrderService { + Channel channel; + + public OrderServiceClient(@GrpcClient("OrderService") Channel channel) { + this.channel = channel; + } + + public static MethodDescriptor CHAT = + MethodDescriptor.newBuilder(ChatMessage.MARSHALLER, ChatMessage.MARSHALLER) + .setType(MethodType.BIDI_STREAMING) + .setFullMethodName("testgrpc.OrderService/Chat") + .build(); + + public static MethodDescriptor CREATE_ORDER = + MethodDescriptor.newBuilder(CreateOrderRequest.MARSHALLER, CreateOrderResponse.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.OrderService/CreateOrder") + .build(); + + public static MethodDescriptor GET_CUSTOMER = + MethodDescriptor.newBuilder(GetCustomerRequest.MARSHALLER, GetCustomerResponse.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.OrderService/GetCustomer") + .build(); + + public static MethodDescriptor LIST_ORDERS = + MethodDescriptor.newBuilder(ListOrdersRequest.MARSHALLER, OrderUpdate.MARSHALLER) + .setType(MethodType.SERVER_STREAMING) + .setFullMethodName("testgrpc.OrderService/ListOrders") + .build(); + + public static MethodDescriptor SUBMIT_ORDERS = + MethodDescriptor.newBuilder(CreateOrderRequest.MARSHALLER, OrderSummary.MARSHALLER) + .setType(MethodType.CLIENT_STREAMING) + .setFullMethodName("testgrpc.OrderService/SubmitOrders") + .build(); + + @Override + public GetCustomerResponse getCustomer(GetCustomerRequest request) { + return ClientCalls.blockingUnaryCall( + channel, OrderServiceClient.GET_CUSTOMER, CallOptions.DEFAULT, request); + } + + @Override + public CreateOrderResponse createOrder(CreateOrderRequest request) { + return ClientCalls.blockingUnaryCall( + channel, OrderServiceClient.CREATE_ORDER, CallOptions.DEFAULT, request); + } + + @Override + public Iterator listOrders(ListOrdersRequest request) { + return ClientCalls.blockingServerStreamingCall( + channel, OrderServiceClient.LIST_ORDERS, CallOptions.DEFAULT, request); + } + + @Override + public OrderSummary submitOrders(Iterator requests) { + throw new UnsupportedOperationException( + "Client streaming not yet implemented in client wrapper"); + } + + @Override + public Iterator chat(Iterator requests) { + throw new UnsupportedOperationException("Bidi streaming not yet implemented in client wrapper"); + } +} diff --git a/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/OrderServiceServer.java b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/OrderServiceServer.java new file mode 100644 index 0000000000..82cf35a386 --- /dev/null +++ b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/OrderServiceServer.java @@ -0,0 +1,95 @@ +package com.example.grpc; + +import io.grpc.BindableService; +import io.grpc.MethodDescriptor; +import io.grpc.MethodDescriptor.MethodType; +import io.grpc.ServerServiceDefinition; +import io.grpc.stub.ServerCalls; +import io.quarkus.grpc.GrpcService; +import jakarta.inject.Singleton; + +/** gRPC server adapter for OrderService - delegates to clean service interface */ +@GrpcService +@Singleton +public class OrderServiceServer implements BindableService { + OrderService delegate; + + public OrderServiceServer(OrderService delegate) { + this.delegate = delegate; + } + + public static MethodDescriptor CHAT = + MethodDescriptor.newBuilder(ChatMessage.MARSHALLER, ChatMessage.MARSHALLER) + .setType(MethodType.BIDI_STREAMING) + .setFullMethodName("testgrpc.OrderService/Chat") + .build(); + + public static MethodDescriptor CREATE_ORDER = + MethodDescriptor.newBuilder(CreateOrderRequest.MARSHALLER, CreateOrderResponse.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.OrderService/CreateOrder") + .build(); + + public static MethodDescriptor GET_CUSTOMER = + MethodDescriptor.newBuilder(GetCustomerRequest.MARSHALLER, GetCustomerResponse.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.OrderService/GetCustomer") + .build(); + + public static MethodDescriptor LIST_ORDERS = + MethodDescriptor.newBuilder(ListOrdersRequest.MARSHALLER, OrderUpdate.MARSHALLER) + .setType(MethodType.SERVER_STREAMING) + .setFullMethodName("testgrpc.OrderService/ListOrders") + .build(); + + public static MethodDescriptor SUBMIT_ORDERS = + MethodDescriptor.newBuilder(CreateOrderRequest.MARSHALLER, OrderSummary.MARSHALLER) + .setType(MethodType.CLIENT_STREAMING) + .setFullMethodName("testgrpc.OrderService/SubmitOrders") + .build(); + + @Override + public ServerServiceDefinition bindService() { + return ServerServiceDefinition.builder("testgrpc.OrderService") + .addMethod( + OrderServiceServer.GET_CUSTOMER, + ServerCalls.asyncUnaryCall( + (request, responseObserver) -> { + responseObserver.onNext(delegate.getCustomer(request)); + responseObserver.onCompleted(); + })) + .addMethod( + OrderServiceServer.CREATE_ORDER, + ServerCalls.asyncUnaryCall( + (request, responseObserver) -> { + responseObserver.onNext(delegate.createOrder(request)); + responseObserver.onCompleted(); + })) + .addMethod( + OrderServiceServer.LIST_ORDERS, + ServerCalls.asyncServerStreamingCall( + (request, responseObserver) -> { + var results = delegate.listOrders(request); + while (results.hasNext()) { + responseObserver.onNext(results.next()); + } + ; + responseObserver.onCompleted(); + })) + .addMethod( + OrderServiceServer.SUBMIT_ORDERS, + ServerCalls.asyncClientStreamingCall( + responseObserver -> { + throw new UnsupportedOperationException( + "Client streaming not yet implemented in server adapter"); + })) + .addMethod( + OrderServiceServer.CHAT, + ServerCalls.asyncBidiStreamingCall( + responseObserver -> { + throw new UnsupportedOperationException( + "Bidi streaming not yet implemented in server adapter"); + })) + .build(); + } +} diff --git a/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/OrderStatus.java b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/OrderStatus.java new file mode 100644 index 0000000000..c0496984dd --- /dev/null +++ b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/OrderStatus.java @@ -0,0 +1,73 @@ +package com.example.grpc; + +public enum OrderStatus { + ORDER_STATUS_UNSPECIFIED("ORDER_STATUS_UNSPECIFIED"), + ORDER_STATUS_PENDING("ORDER_STATUS_PENDING"), + ORDER_STATUS_PROCESSING("ORDER_STATUS_PROCESSING"), + ORDER_STATUS_SHIPPED("ORDER_STATUS_SHIPPED"), + ORDER_STATUS_DELIVERED("ORDER_STATUS_DELIVERED"), + ORDER_STATUS_CANCELLED("ORDER_STATUS_CANCELLED"); + final java.lang.String value; + + public java.lang.String value() { + return value; + } + + OrderStatus(java.lang.String value) { + this.value = value; + } + + public static final java.lang.String Names = + java.util.Arrays.stream(OrderStatus.values()) + .map(x -> x.value) + .collect(java.util.stream.Collectors.joining(", ")); + public static final java.util.Map ByName = + java.util.Arrays.stream(OrderStatus.values()) + .collect(java.util.stream.Collectors.toMap(n -> n.value, n -> n)); + + public Integer toValue() { + if (this.toString().equals("ORDER_STATUS_UNSPECIFIED")) { + return 0; + } else if (this.toString().equals("ORDER_STATUS_PENDING")) { + return 1; + } else if (this.toString().equals("ORDER_STATUS_PROCESSING")) { + return 2; + } else if (this.toString().equals("ORDER_STATUS_SHIPPED")) { + return 3; + } else if (this.toString().equals("ORDER_STATUS_DELIVERED")) { + return 4; + } else if (this.toString().equals("ORDER_STATUS_CANCELLED")) { + return 5; + } else { + return 0; + } + } + + public static OrderStatus fromValue(Integer value) { + if (value == 0) { + return OrderStatus.ORDER_STATUS_UNSPECIFIED; + } else if (value == 1) { + return OrderStatus.ORDER_STATUS_PENDING; + } else if (value == 2) { + return OrderStatus.ORDER_STATUS_PROCESSING; + } else if (value == 3) { + return OrderStatus.ORDER_STATUS_SHIPPED; + } else if (value == 4) { + return OrderStatus.ORDER_STATUS_DELIVERED; + } else if (value == 5) { + return OrderStatus.ORDER_STATUS_CANCELLED; + } else { + throw new IllegalArgumentException("Unknown enum value: " + value); + } + } + ; + + public static OrderStatus force(java.lang.String str) { + if (ByName.containsKey(str)) { + return ByName.get(str); + } else { + throw new RuntimeException( + "'" + str + "' does not match any of the following legal values: " + Names); + } + } +} diff --git a/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/OrderSummary.java b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/OrderSummary.java new file mode 100644 index 0000000000..e2750a4e1d --- /dev/null +++ b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/OrderSummary.java @@ -0,0 +1,74 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record OrderSummary(Integer totalOrders, Long totalAmountCents) { + public OrderSummary withTotalOrders(Integer totalOrders) { + return new OrderSummary(totalOrders, totalAmountCents); + } + + public OrderSummary withTotalAmountCents(Long totalAmountCents) { + return new OrderSummary(totalOrders, totalAmountCents); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(OrderSummary value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public OrderSummary parse(InputStream stream) { + try { + return OrderSummary.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static OrderSummary parseFrom(CodedInputStream input) throws IOException { + Integer totalOrders = 0; + Long totalAmountCents = 0L; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + totalOrders = input.readInt32(); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + totalAmountCents = input.readInt64(); + } else { + input.skipField(tag); + } + ; + } + ; + return new OrderSummary(totalOrders, totalAmountCents); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeInt32Size(1, this.totalOrders()); + size = size + CodedOutputStream.computeInt64Size(2, this.totalAmountCents()); + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeInt32(1, this.totalOrders()); + output.writeInt64(2, this.totalAmountCents()); + } +} diff --git a/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/OrderUpdate.java b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/OrderUpdate.java new file mode 100644 index 0000000000..ed13d01723 --- /dev/null +++ b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/OrderUpdate.java @@ -0,0 +1,114 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.time.Instant; + +public record OrderUpdate(String orderId, OrderStatus status, Instant updatedAt) { + public OrderUpdate withOrderId(String orderId) { + return new OrderUpdate(orderId, status, updatedAt); + } + + public OrderUpdate withStatus(OrderStatus status) { + return new OrderUpdate(orderId, status, updatedAt); + } + + public OrderUpdate withUpdatedAt(Instant updatedAt) { + return new OrderUpdate(orderId, status, updatedAt); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(OrderUpdate value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public OrderUpdate parse(InputStream stream) { + try { + return OrderUpdate.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static OrderUpdate parseFrom(CodedInputStream input) throws IOException { + String orderId = ""; + OrderStatus status = OrderStatus.fromValue(0); + Instant updatedAt = Instant.EPOCH; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + orderId = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + status = OrderStatus.fromValue(input.readEnum()); + } else if (WireFormat.getTagFieldNumber(tag) == 3) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + var _tsSeconds = 0L; + var _tsNanos = 0; + while (!input.isAtEnd()) { + var _tsTag = input.readTag(); + if (WireFormat.getTagFieldNumber(_tsTag) == 1) { + _tsSeconds = input.readInt64(); + } else if (WireFormat.getTagFieldNumber(_tsTag) == 2) { + _tsNanos = input.readInt32(); + } else { + input.skipField(_tsTag); + } + ; + } + ; + updatedAt = Instant.ofEpochSecond(_tsSeconds, (long) (_tsNanos)); + input.popLimit(_oldLimit); + ; + } else { + input.skipField(tag); + } + ; + } + ; + return new OrderUpdate(orderId, status, updatedAt); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeStringSize(1, this.orderId()); + size = size + CodedOutputStream.computeEnumSize(2, this.status().toValue()); + size = + size + + CodedOutputStream.computeTagSize(3) + + CodedOutputStream.computeUInt32SizeNoTag( + CodedOutputStream.computeInt64Size(1, this.updatedAt().getEpochSecond()) + + CodedOutputStream.computeInt32Size(2, this.updatedAt().getNano())) + + CodedOutputStream.computeInt64Size(1, this.updatedAt().getEpochSecond()) + + CodedOutputStream.computeInt32Size(2, this.updatedAt().getNano()); + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeString(1, this.orderId()); + output.writeEnum(2, this.status().toValue()); + output.writeTag(3, 2); + output.writeUInt32NoTag( + CodedOutputStream.computeInt64Size(1, this.updatedAt().getEpochSecond()) + + CodedOutputStream.computeInt32Size(2, this.updatedAt().getNano())); + output.writeInt64(1, this.updatedAt().getEpochSecond()); + output.writeInt32(2, this.updatedAt().getNano()); + } +} diff --git a/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/Outer.java b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/Outer.java new file mode 100644 index 0000000000..e60998c822 --- /dev/null +++ b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/Outer.java @@ -0,0 +1,89 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record Outer(String name, Inner inner) { + public Outer withName(String name) { + return new Outer(name, inner); + } + + public Outer withInner(Inner inner) { + return new Outer(name, inner); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(Outer value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public Outer parse(InputStream stream) { + try { + return Outer.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static Outer parseFrom(CodedInputStream input) throws IOException { + String name = ""; + Inner inner = null; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + name = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + inner = Inner.parseFrom(input); + input.popLimit(_oldLimit); + ; + } else { + input.skipField(tag); + } + ; + } + ; + return new Outer(name, inner); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeStringSize(1, this.name()); + if (!this.inner().equals(null)) { + size = + size + + CodedOutputStream.computeTagSize(2) + + CodedOutputStream.computeUInt32SizeNoTag(this.inner().getSerializedSize()) + + this.inner().getSerializedSize(); + } + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeString(1, this.name()); + if (!this.inner().equals(null)) { + output.writeTag(2, 2); + output.writeUInt32NoTag(this.inner().getSerializedSize()); + this.inner().writeTo(output); + ; + } + } +} diff --git a/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/PaymentMethod.java b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/PaymentMethod.java new file mode 100644 index 0000000000..329bb47f66 --- /dev/null +++ b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/PaymentMethod.java @@ -0,0 +1,132 @@ +package com.example.grpc; + +import com.example.grpc.PaymentMethodMethod.BankTransferValue; +import com.example.grpc.PaymentMethodMethod.CreditCardValue; +import com.example.grpc.PaymentMethodMethod.WalletValue; +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record PaymentMethod(String id, PaymentMethodMethod method) { + public PaymentMethod withId(String id) { + return new PaymentMethod(id, method); + } + + public PaymentMethod withMethod(PaymentMethodMethod method) { + return new PaymentMethod(id, method); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(PaymentMethod value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public PaymentMethod parse(InputStream stream) { + try { + return PaymentMethod.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static PaymentMethod parseFrom(CodedInputStream input) throws IOException { + String id = ""; + PaymentMethodMethod method = null; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + id = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + method = new CreditCardValue(CreditCard.parseFrom(input)); + input.popLimit(_oldLimit); + ; + } else if (WireFormat.getTagFieldNumber(tag) == 3) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + method = new BankTransferValue(BankTransfer.parseFrom(input)); + input.popLimit(_oldLimit); + ; + } else if (WireFormat.getTagFieldNumber(tag) == 4) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + method = new WalletValue(Wallet.parseFrom(input)); + input.popLimit(_oldLimit); + ; + } else { + input.skipField(tag); + } + ; + } + ; + return new PaymentMethod(id, method); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeStringSize(1, this.id()); + switch (this.method()) { + case null -> {} + case CreditCardValue c -> + size = + size + + CodedOutputStream.computeTagSize(2) + + CodedOutputStream.computeUInt32SizeNoTag(c.creditCard().getSerializedSize()) + + c.creditCard().getSerializedSize(); + case BankTransferValue c -> + size = + size + + CodedOutputStream.computeTagSize(3) + + CodedOutputStream.computeUInt32SizeNoTag(c.bankTransfer().getSerializedSize()) + + c.bankTransfer().getSerializedSize(); + case WalletValue c -> + size = + size + + CodedOutputStream.computeTagSize(4) + + CodedOutputStream.computeUInt32SizeNoTag(c.wallet().getSerializedSize()) + + c.wallet().getSerializedSize(); + } + ; + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeString(1, this.id()); + switch (this.method()) { + case null -> {} + case CreditCardValue c -> { + output.writeTag(2, 2); + output.writeUInt32NoTag(c.creditCard().getSerializedSize()); + c.creditCard().writeTo(output); + } + case BankTransferValue c -> { + output.writeTag(3, 2); + output.writeUInt32NoTag(c.bankTransfer().getSerializedSize()); + c.bankTransfer().writeTo(output); + } + case WalletValue c -> { + output.writeTag(4, 2); + output.writeUInt32NoTag(c.wallet().getSerializedSize()); + c.wallet().writeTo(output); + } + } + ; + } +} diff --git a/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/PaymentMethodMethod.java b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/PaymentMethodMethod.java new file mode 100644 index 0000000000..7772f87dae --- /dev/null +++ b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/PaymentMethodMethod.java @@ -0,0 +1,25 @@ +package com.example.grpc; + +/** OneOf type for method */ +public sealed interface PaymentMethodMethod + permits PaymentMethodMethod.CreditCardValue, + PaymentMethodMethod.BankTransferValue, + PaymentMethodMethod.WalletValue { + record BankTransferValue(BankTransfer bankTransfer) implements PaymentMethodMethod { + public BankTransferValue withBankTransfer(BankTransfer bankTransfer) { + return new BankTransferValue(bankTransfer); + } + } + + record CreditCardValue(CreditCard creditCard) implements PaymentMethodMethod { + public CreditCardValue withCreditCard(CreditCard creditCard) { + return new CreditCardValue(creditCard); + } + } + + record WalletValue(Wallet wallet) implements PaymentMethodMethod { + public WalletValue withWallet(Wallet wallet) { + return new WalletValue(wallet); + } + } +} diff --git a/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/Priority.java b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/Priority.java new file mode 100644 index 0000000000..d11a842342 --- /dev/null +++ b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/Priority.java @@ -0,0 +1,68 @@ +package com.example.grpc; + +public enum Priority { + PRIORITY_UNSPECIFIED("PRIORITY_UNSPECIFIED"), + PRIORITY_LOW("PRIORITY_LOW"), + PRIORITY_MEDIUM("PRIORITY_MEDIUM"), + PRIORITY_HIGH("PRIORITY_HIGH"), + PRIORITY_CRITICAL("PRIORITY_CRITICAL"); + final java.lang.String value; + + public java.lang.String value() { + return value; + } + + Priority(java.lang.String value) { + this.value = value; + } + + public static final java.lang.String Names = + java.util.Arrays.stream(Priority.values()) + .map(x -> x.value) + .collect(java.util.stream.Collectors.joining(", ")); + public static final java.util.Map ByName = + java.util.Arrays.stream(Priority.values()) + .collect(java.util.stream.Collectors.toMap(n -> n.value, n -> n)); + + public Integer toValue() { + if (this.toString().equals("PRIORITY_UNSPECIFIED")) { + return 0; + } else if (this.toString().equals("PRIORITY_LOW")) { + return 1; + } else if (this.toString().equals("PRIORITY_MEDIUM")) { + return 2; + } else if (this.toString().equals("PRIORITY_HIGH")) { + return 3; + } else if (this.toString().equals("PRIORITY_CRITICAL")) { + return 4; + } else { + return 0; + } + } + + public static Priority fromValue(Integer value) { + if (value == 0) { + return Priority.PRIORITY_UNSPECIFIED; + } else if (value == 1) { + return Priority.PRIORITY_LOW; + } else if (value == 2) { + return Priority.PRIORITY_MEDIUM; + } else if (value == 3) { + return Priority.PRIORITY_HIGH; + } else if (value == 4) { + return Priority.PRIORITY_CRITICAL; + } else { + throw new IllegalArgumentException("Unknown enum value: " + value); + } + } + ; + + public static Priority force(java.lang.String str) { + if (ByName.containsKey(str)) { + return ByName.get(str); + } else { + throw new RuntimeException( + "'" + str + "' does not match any of the following legal values: " + Names); + } + } +} diff --git a/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/ScalarTypes.java b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/ScalarTypes.java new file mode 100644 index 0000000000..3117c27eda --- /dev/null +++ b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/ScalarTypes.java @@ -0,0 +1,447 @@ +package com.example.grpc; + +import com.google.protobuf.ByteString; +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record ScalarTypes( + Double doubleVal, + Float floatVal, + Integer int32Val, + Long int64Val, + Integer uint32Val, + Long uint64Val, + Integer sint32Val, + Long sint64Val, + Integer fixed32Val, + Long fixed64Val, + Integer sfixed32Val, + Long sfixed64Val, + Boolean boolVal, + String stringVal, + ByteString bytesVal) { + public ScalarTypes withDoubleVal(Double doubleVal) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public ScalarTypes withFloatVal(Float floatVal) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public ScalarTypes withInt32Val(Integer int32Val) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public ScalarTypes withInt64Val(Long int64Val) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public ScalarTypes withUint32Val(Integer uint32Val) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public ScalarTypes withUint64Val(Long uint64Val) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public ScalarTypes withSint32Val(Integer sint32Val) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public ScalarTypes withSint64Val(Long sint64Val) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public ScalarTypes withFixed32Val(Integer fixed32Val) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public ScalarTypes withFixed64Val(Long fixed64Val) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public ScalarTypes withSfixed32Val(Integer sfixed32Val) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public ScalarTypes withSfixed64Val(Long sfixed64Val) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public ScalarTypes withBoolVal(Boolean boolVal) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public ScalarTypes withStringVal(String stringVal) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public ScalarTypes withBytesVal(ByteString bytesVal) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(ScalarTypes value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public ScalarTypes parse(InputStream stream) { + try { + return ScalarTypes.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static ScalarTypes parseFrom(CodedInputStream input) throws IOException { + Double doubleVal = 0.0; + Float floatVal = 0.0f; + Integer int32Val = 0; + Long int64Val = 0L; + Integer uint32Val = 0; + Long uint64Val = 0L; + Integer sint32Val = 0; + Long sint64Val = 0L; + Integer fixed32Val = 0; + Long fixed64Val = 0L; + Integer sfixed32Val = 0; + Long sfixed64Val = 0L; + Boolean boolVal = false; + String stringVal = ""; + ByteString bytesVal = ByteString.EMPTY; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + doubleVal = input.readDouble(); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + floatVal = input.readFloat(); + } else if (WireFormat.getTagFieldNumber(tag) == 3) { + int32Val = input.readInt32(); + } else if (WireFormat.getTagFieldNumber(tag) == 4) { + int64Val = input.readInt64(); + } else if (WireFormat.getTagFieldNumber(tag) == 5) { + uint32Val = input.readUInt32(); + } else if (WireFormat.getTagFieldNumber(tag) == 6) { + uint64Val = input.readUInt64(); + } else if (WireFormat.getTagFieldNumber(tag) == 7) { + sint32Val = input.readSInt32(); + } else if (WireFormat.getTagFieldNumber(tag) == 8) { + sint64Val = input.readSInt64(); + } else if (WireFormat.getTagFieldNumber(tag) == 9) { + fixed32Val = input.readFixed32(); + } else if (WireFormat.getTagFieldNumber(tag) == 10) { + fixed64Val = input.readFixed64(); + } else if (WireFormat.getTagFieldNumber(tag) == 11) { + sfixed32Val = input.readSFixed32(); + } else if (WireFormat.getTagFieldNumber(tag) == 12) { + sfixed64Val = input.readSFixed64(); + } else if (WireFormat.getTagFieldNumber(tag) == 13) { + boolVal = input.readBool(); + } else if (WireFormat.getTagFieldNumber(tag) == 14) { + stringVal = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 15) { + bytesVal = input.readBytes(); + } else { + input.skipField(tag); + } + ; + } + ; + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeDoubleSize(1, this.doubleVal()); + size = size + CodedOutputStream.computeFloatSize(2, this.floatVal()); + size = size + CodedOutputStream.computeInt32Size(3, this.int32Val()); + size = size + CodedOutputStream.computeInt64Size(4, this.int64Val()); + size = size + CodedOutputStream.computeUInt32Size(5, this.uint32Val()); + size = size + CodedOutputStream.computeUInt64Size(6, this.uint64Val()); + size = size + CodedOutputStream.computeSInt32Size(7, this.sint32Val()); + size = size + CodedOutputStream.computeSInt64Size(8, this.sint64Val()); + size = size + CodedOutputStream.computeFixed32Size(9, this.fixed32Val()); + size = size + CodedOutputStream.computeFixed64Size(10, this.fixed64Val()); + size = size + CodedOutputStream.computeSFixed32Size(11, this.sfixed32Val()); + size = size + CodedOutputStream.computeSFixed64Size(12, this.sfixed64Val()); + size = size + CodedOutputStream.computeBoolSize(13, this.boolVal()); + size = size + CodedOutputStream.computeStringSize(14, this.stringVal()); + size = size + CodedOutputStream.computeBytesSize(15, this.bytesVal()); + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeDouble(1, this.doubleVal()); + output.writeFloat(2, this.floatVal()); + output.writeInt32(3, this.int32Val()); + output.writeInt64(4, this.int64Val()); + output.writeUInt32(5, this.uint32Val()); + output.writeUInt64(6, this.uint64Val()); + output.writeSInt32(7, this.sint32Val()); + output.writeSInt64(8, this.sint64Val()); + output.writeFixed32(9, this.fixed32Val()); + output.writeFixed64(10, this.fixed64Val()); + output.writeSFixed32(11, this.sfixed32Val()); + output.writeSFixed64(12, this.sfixed64Val()); + output.writeBool(13, this.boolVal()); + output.writeString(14, this.stringVal()); + output.writeBytes(15, this.bytesVal()); + } +} diff --git a/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/Wallet.java b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/Wallet.java new file mode 100644 index 0000000000..52ce7c24d5 --- /dev/null +++ b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/Wallet.java @@ -0,0 +1,74 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record Wallet(String walletId, String provider) { + public Wallet withWalletId(String walletId) { + return new Wallet(walletId, provider); + } + + public Wallet withProvider(String provider) { + return new Wallet(walletId, provider); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(Wallet value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public Wallet parse(InputStream stream) { + try { + return Wallet.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static Wallet parseFrom(CodedInputStream input) throws IOException { + String walletId = ""; + String provider = ""; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + walletId = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + provider = input.readString(); + } else { + input.skipField(tag); + } + ; + } + ; + return new Wallet(walletId, provider); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeStringSize(1, this.walletId()); + size = size + CodedOutputStream.computeStringSize(2, this.provider()); + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeString(1, this.walletId()); + output.writeString(2, this.provider()); + } +} diff --git a/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/WellKnownTypesMessage.java b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/WellKnownTypesMessage.java new file mode 100644 index 0000000000..f0ee16f7e5 --- /dev/null +++ b/testers/grpc/java-quarkus/generated-and-checked-in/com/example/grpc/WellKnownTypesMessage.java @@ -0,0 +1,226 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.time.Duration; +import java.time.Instant; +import java.util.Optional; + +public record WellKnownTypesMessage( + Instant createdAt, + Duration ttl, + Optional nullableString, + Optional nullableInt, + Optional nullableBool) { + public WellKnownTypesMessage withCreatedAt(Instant createdAt) { + return new WellKnownTypesMessage(createdAt, ttl, nullableString, nullableInt, nullableBool); + } + + public WellKnownTypesMessage withTtl(Duration ttl) { + return new WellKnownTypesMessage(createdAt, ttl, nullableString, nullableInt, nullableBool); + } + + public WellKnownTypesMessage withNullableString(Optional nullableString) { + return new WellKnownTypesMessage(createdAt, ttl, nullableString, nullableInt, nullableBool); + } + + public WellKnownTypesMessage withNullableInt(Optional nullableInt) { + return new WellKnownTypesMessage(createdAt, ttl, nullableString, nullableInt, nullableBool); + } + + public WellKnownTypesMessage withNullableBool(Optional nullableBool) { + return new WellKnownTypesMessage(createdAt, ttl, nullableString, nullableInt, nullableBool); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(WellKnownTypesMessage value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public WellKnownTypesMessage parse(InputStream stream) { + try { + return WellKnownTypesMessage.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static WellKnownTypesMessage parseFrom(CodedInputStream input) throws IOException { + Instant createdAt = Instant.EPOCH; + Duration ttl = Duration.ZERO; + Optional nullableString = Optional.empty(); + Optional nullableInt = Optional.empty(); + Optional nullableBool = Optional.empty(); + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + var _tsSeconds = 0L; + var _tsNanos = 0; + while (!input.isAtEnd()) { + var _tsTag = input.readTag(); + if (WireFormat.getTagFieldNumber(_tsTag) == 1) { + _tsSeconds = input.readInt64(); + } else if (WireFormat.getTagFieldNumber(_tsTag) == 2) { + _tsNanos = input.readInt32(); + } else { + input.skipField(_tsTag); + } + ; + } + ; + createdAt = Instant.ofEpochSecond(_tsSeconds, (long) (_tsNanos)); + input.popLimit(_oldLimit); + ; + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + var _durSeconds = 0L; + var _durNanos = 0; + while (!input.isAtEnd()) { + var _durTag = input.readTag(); + if (WireFormat.getTagFieldNumber(_durTag) == 1) { + _durSeconds = input.readInt64(); + } else if (WireFormat.getTagFieldNumber(_durTag) == 2) { + _durNanos = input.readInt32(); + } else { + input.skipField(_durTag); + } + ; + } + ; + ttl = Duration.ofSeconds(_durSeconds, (long) (_durNanos)); + input.popLimit(_oldLimit); + ; + } else if (WireFormat.getTagFieldNumber(tag) == 3) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + input.readTag(); + nullableString = Optional.of(input.readString()); + input.popLimit(_oldLimit); + ; + } else if (WireFormat.getTagFieldNumber(tag) == 4) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + input.readTag(); + nullableInt = Optional.of(input.readInt32()); + input.popLimit(_oldLimit); + ; + } else if (WireFormat.getTagFieldNumber(tag) == 5) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + input.readTag(); + nullableBool = Optional.of(input.readBool()); + input.popLimit(_oldLimit); + ; + } else { + input.skipField(tag); + } + ; + } + ; + return new WellKnownTypesMessage(createdAt, ttl, nullableString, nullableInt, nullableBool); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = + size + + CodedOutputStream.computeTagSize(1) + + CodedOutputStream.computeUInt32SizeNoTag( + CodedOutputStream.computeInt64Size(1, this.createdAt().getEpochSecond()) + + CodedOutputStream.computeInt32Size(2, this.createdAt().getNano())) + + CodedOutputStream.computeInt64Size(1, this.createdAt().getEpochSecond()) + + CodedOutputStream.computeInt32Size(2, this.createdAt().getNano()); + size = + size + + CodedOutputStream.computeTagSize(2) + + CodedOutputStream.computeUInt32SizeNoTag( + CodedOutputStream.computeInt64Size(1, this.ttl().getSeconds()) + + CodedOutputStream.computeInt32Size(2, this.ttl().getNano())) + + CodedOutputStream.computeInt64Size(1, this.ttl().getSeconds()) + + CodedOutputStream.computeInt32Size(2, this.ttl().getNano()); + if (this.nullableString().isPresent()) { + var v = this.nullableString().get(); + size = + size + + CodedOutputStream.computeTagSize(3) + + CodedOutputStream.computeUInt32SizeNoTag(CodedOutputStream.computeStringSize(1, v)) + + CodedOutputStream.computeStringSize(1, v); + ; + } + if (this.nullableInt().isPresent()) { + var v = this.nullableInt().get(); + size = + size + + CodedOutputStream.computeTagSize(4) + + CodedOutputStream.computeUInt32SizeNoTag(CodedOutputStream.computeInt32Size(1, v)) + + CodedOutputStream.computeInt32Size(1, v); + ; + } + if (this.nullableBool().isPresent()) { + var v = this.nullableBool().get(); + size = + size + + CodedOutputStream.computeTagSize(5) + + CodedOutputStream.computeUInt32SizeNoTag(CodedOutputStream.computeBoolSize(1, v)) + + CodedOutputStream.computeBoolSize(1, v); + ; + } + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeTag(1, 2); + output.writeUInt32NoTag( + CodedOutputStream.computeInt64Size(1, this.createdAt().getEpochSecond()) + + CodedOutputStream.computeInt32Size(2, this.createdAt().getNano())); + output.writeInt64(1, this.createdAt().getEpochSecond()); + output.writeInt32(2, this.createdAt().getNano()); + output.writeTag(2, 2); + output.writeUInt32NoTag( + CodedOutputStream.computeInt64Size(1, this.ttl().getSeconds()) + + CodedOutputStream.computeInt32Size(2, this.ttl().getNano())); + output.writeInt64(1, this.ttl().getSeconds()); + output.writeInt32(2, this.ttl().getNano()); + if (this.nullableString().isPresent()) { + var v = this.nullableString().get(); + output.writeTag(3, 2); + output.writeUInt32NoTag(CodedOutputStream.computeStringSize(1, v)); + output.writeString(1, v); + ; + } + if (this.nullableInt().isPresent()) { + var v = this.nullableInt().get(); + output.writeTag(4, 2); + output.writeUInt32NoTag(CodedOutputStream.computeInt32Size(1, v)); + output.writeInt32(1, v); + ; + } + if (this.nullableBool().isPresent()) { + var v = this.nullableBool().get(); + output.writeTag(5, 2); + output.writeUInt32NoTag(CodedOutputStream.computeBoolSize(1, v)); + output.writeBool(1, v); + ; + } + } +} diff --git a/testers/grpc/java-quarkus/src/java/com/example/grpc/GrpcIntegrationTest.java b/testers/grpc/java-quarkus/src/java/com/example/grpc/GrpcIntegrationTest.java new file mode 100644 index 0000000000..5fb9d58f5f --- /dev/null +++ b/testers/grpc/java-quarkus/src/java/com/example/grpc/GrpcIntegrationTest.java @@ -0,0 +1,520 @@ +package com.example.grpc; + +import static org.junit.Assert.*; + +import com.google.protobuf.ByteString; +import io.grpc.ManagedChannel; +import io.grpc.Server; +import io.grpc.inprocess.InProcessChannelBuilder; +import io.grpc.inprocess.InProcessServerBuilder; +import java.time.Duration; +import java.time.Instant; +import java.util.*; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class GrpcIntegrationTest { + + private Server server; + private ManagedChannel channel; + private OrderServiceClient orderClient; + private EchoServiceClient echoClient; + + private final Customer testCustomer = + new Customer(CustomerId.valueOf("CUST-123"), "John Doe", "john@example.com"); + + @Before + public void setUp() throws Exception { + String serverName = InProcessServerBuilder.generateName(); + + OrderService orderImpl = + new OrderService() { + @Override + public GetCustomerResponse getCustomer(GetCustomerRequest request) { + return new GetCustomerResponse( + new Customer( + CustomerId.valueOf(request.customerId()), "John Doe", "john@example.com")); + } + + @Override + public CreateOrderResponse createOrder(CreateOrderRequest request) { + return new CreateOrderResponse( + request.order().orderId().unwrap(), OrderStatus.ORDER_STATUS_PENDING); + } + + @Override + public Iterator listOrders(ListOrdersRequest request) { + List updates = new ArrayList<>(); + updates.add( + new OrderUpdate( + "ORD-1", OrderStatus.ORDER_STATUS_PENDING, Instant.ofEpochSecond(1000, 500))); + updates.add( + new OrderUpdate( + "ORD-2", OrderStatus.ORDER_STATUS_SHIPPED, Instant.ofEpochSecond(2000, 1000))); + updates.add( + new OrderUpdate( + "ORD-3", OrderStatus.ORDER_STATUS_DELIVERED, Instant.ofEpochSecond(3000, 0))); + return updates.iterator(); + } + + @Override + public OrderSummary submitOrders(Iterator requests) { + throw new UnsupportedOperationException(); + } + + @Override + public Iterator chat(Iterator requests) { + throw new UnsupportedOperationException(); + } + }; + + EchoService echoImpl = + new EchoService() { + @Override + public ScalarTypes echoScalarTypes(ScalarTypes request) { + return request; + } + + @Override + public Customer echoCustomer(Customer request) { + return request; + } + + @Override + public Order echoOrder(Order request) { + return request; + } + + @Override + public Inventory echoInventory(Inventory request) { + return request; + } + + @Override + public Outer echoOuter(Outer request) { + return request; + } + + @Override + public OptionalFields echoOptionalFields(OptionalFields request) { + return request; + } + + @Override + public WellKnownTypesMessage echoWellKnownTypes(WellKnownTypesMessage request) { + return request; + } + + @Override + public PaymentMethod echoPaymentMethod(PaymentMethod request) { + return request; + } + + @Override + public Notification echoNotification(Notification request) { + return request; + } + }; + + server = + InProcessServerBuilder.forName(serverName) + .directExecutor() + .addService(new OrderServiceServer(orderImpl)) + .addService(new EchoServiceServer(echoImpl)) + .build() + .start(); + + channel = InProcessChannelBuilder.forName(serverName).directExecutor().build(); + orderClient = new OrderServiceClient(channel); + echoClient = new EchoServiceClient(channel); + } + + @After + public void tearDown() throws Exception { + channel.shutdownNow(); + server.shutdownNow(); + } + + // ---- gRPC service tests ---- + + @Test + public void testGetCustomer() { + GetCustomerResponse response = orderClient.getCustomer(new GetCustomerRequest("CUST-123")); + assertNotNull(response); + assertNotNull(response.customer()); + assertEquals("CUST-123", response.customer().customerId().unwrap()); + assertEquals("John Doe", response.customer().name()); + assertEquals("john@example.com", response.customer().email()); + } + + @Test + public void testCreateOrder() { + Order order = + new Order( + OrderId.valueOf("ORD-42"), + CustomerId.valueOf("CUST-1"), + 9999L, + Instant.ofEpochSecond(1700000000L, 123456789)); + + CreateOrderResponse response = orderClient.createOrder(new CreateOrderRequest(order)); + assertNotNull(response); + assertEquals("ORD-42", response.orderId()); + assertEquals(OrderStatus.ORDER_STATUS_PENDING, response.status()); + } + + @Test + public void testListOrders() { + Iterator updates = orderClient.listOrders(new ListOrdersRequest("CUST-123", 10)); + List results = new ArrayList<>(); + updates.forEachRemaining(results::add); + + assertEquals(3, results.size()); + assertEquals("ORD-1", results.get(0).orderId()); + assertEquals(OrderStatus.ORDER_STATUS_PENDING, results.get(0).status()); + assertEquals("ORD-2", results.get(1).orderId()); + assertEquals(OrderStatus.ORDER_STATUS_SHIPPED, results.get(1).status()); + assertEquals("ORD-3", results.get(2).orderId()); + assertEquals(OrderStatus.ORDER_STATUS_DELIVERED, results.get(2).status()); + } + + // ---- Echo round-trip tests ---- + + @Test + public void testEchoCustomer() { + Customer parsed = echoClient.echoCustomer(testCustomer); + assertEquals(testCustomer, parsed); + } + + @Test + public void testEchoOrder() { + Order order = + new Order( + OrderId.valueOf("ORD-1"), + CustomerId.valueOf("CUST-1"), + 5000L, + Instant.ofEpochSecond(1700000000L, 123456789)); + Order parsed = echoClient.echoOrder(order); + assertEquals(order.orderId(), parsed.orderId()); + assertEquals(order.customerId(), parsed.customerId()); + assertEquals(order.amountCents(), parsed.amountCents()); + assertEquals(order.createdAt(), parsed.createdAt()); + } + + // ---- Scalar types ---- + + @Test + public void testEchoScalarTypes() { + ScalarTypes scalars = + new ScalarTypes( + 3.14, + 2.71f, + 42, + 9876543210L, + 100, + 200L, + -50, + -100L, + 999, + 888L, + -777, + -666L, + true, + "hello world", + ByteString.copyFromUtf8("binary data")); + ScalarTypes parsed = echoClient.echoScalarTypes(scalars); + assertEquals(scalars.doubleVal(), parsed.doubleVal(), 0.0001); + assertEquals(scalars.floatVal(), parsed.floatVal(), 0.0001f); + assertEquals(scalars.int32Val(), parsed.int32Val()); + assertEquals(scalars.int64Val(), parsed.int64Val()); + assertEquals(scalars.uint32Val(), parsed.uint32Val()); + assertEquals(scalars.uint64Val(), parsed.uint64Val()); + assertEquals(scalars.sint32Val(), parsed.sint32Val()); + assertEquals(scalars.sint64Val(), parsed.sint64Val()); + assertEquals(scalars.fixed32Val(), parsed.fixed32Val()); + assertEquals(scalars.fixed64Val(), parsed.fixed64Val()); + assertEquals(scalars.sfixed32Val(), parsed.sfixed32Val()); + assertEquals(scalars.sfixed64Val(), parsed.sfixed64Val()); + assertEquals(scalars.boolVal(), parsed.boolVal()); + assertEquals(scalars.stringVal(), parsed.stringVal()); + assertEquals(scalars.bytesVal(), parsed.bytesVal()); + } + + // ---- Enum tests ---- + + @Test + public void testEnumToValueFromValueRoundTrip() { + for (OrderStatus status : OrderStatus.values()) { + Integer wireValue = status.toValue(); + OrderStatus back = OrderStatus.fromValue(wireValue); + assertEquals(status, back); + } + } + + @Test + public void testEnumForce() { + assertEquals(OrderStatus.ORDER_STATUS_PENDING, OrderStatus.force("ORDER_STATUS_PENDING")); + } + + @Test(expected = RuntimeException.class) + public void testEnumForceInvalid() { + OrderStatus.force("NONEXISTENT"); + } + + @Test(expected = IllegalArgumentException.class) + public void testEnumFromValueInvalid() { + OrderStatus.fromValue(999); + } + + @Test + public void testPriorityEnumRoundTrip() { + for (Priority p : Priority.values()) { + Integer wireValue = p.toValue(); + Priority back = Priority.fromValue(wireValue); + assertEquals(p, back); + } + } + + // ---- Optional fields ---- + + @Test + public void testEchoOptionalFieldsAllPresent() { + OptionalFields opt = + new OptionalFields(Optional.of("Alice"), Optional.of(30), Optional.of(testCustomer)); + OptionalFields parsed = echoClient.echoOptionalFields(opt); + assertEquals(Optional.of("Alice"), parsed.name()); + assertEquals(Optional.of(30), parsed.age()); + assertTrue(parsed.customer().isPresent()); + assertEquals("CUST-123", parsed.customer().get().customerId().unwrap()); + } + + @Test + public void testEchoOptionalFieldsAllEmpty() { + OptionalFields opt = new OptionalFields(Optional.empty(), Optional.empty(), Optional.empty()); + OptionalFields parsed = echoClient.echoOptionalFields(opt); + assertEquals(Optional.empty(), parsed.name()); + assertEquals(Optional.empty(), parsed.age()); + assertEquals(Optional.empty(), parsed.customer()); + } + + @Test + public void testEchoOptionalFieldsPartiallyPresent() { + OptionalFields opt = new OptionalFields(Optional.of("Bob"), Optional.empty(), Optional.empty()); + OptionalFields parsed = echoClient.echoOptionalFields(opt); + assertEquals(Optional.of("Bob"), parsed.name()); + assertEquals(Optional.empty(), parsed.age()); + assertEquals(Optional.empty(), parsed.customer()); + } + + // ---- Nested messages ---- + + @Test + public void testEchoOuter() { + Outer outer = new Outer("outer-name", new Inner(42, "inner-desc")); + Outer parsed = echoClient.echoOuter(outer); + assertEquals("outer-name", parsed.name()); + assertNotNull(parsed.inner()); + assertEquals(Integer.valueOf(42), parsed.inner().value()); + assertEquals("inner-desc", parsed.inner().description()); + } + + // ---- OneOf types ---- + + @Test + public void testEchoPaymentMethodCreditCard() { + CreditCard cc = new CreditCard("4111111111111111", "12/25", "123"); + PaymentMethodMethod method = new PaymentMethodMethod.CreditCardValue(cc); + PaymentMethod pm = new PaymentMethod("PAY-1", method); + PaymentMethod parsed = echoClient.echoPaymentMethod(pm); + assertEquals("PAY-1", parsed.id()); + assertTrue(parsed.method() instanceof PaymentMethodMethod.CreditCardValue); + PaymentMethodMethod.CreditCardValue ccv = (PaymentMethodMethod.CreditCardValue) parsed.method(); + assertEquals("4111111111111111", ccv.creditCard().cardNumber()); + assertEquals("12/25", ccv.creditCard().expiryDate()); + assertEquals("123", ccv.creditCard().cvv()); + } + + @Test + public void testEchoPaymentMethodBankTransfer() { + BankTransfer bt = new BankTransfer("123456789", "021000021"); + PaymentMethodMethod method = new PaymentMethodMethod.BankTransferValue(bt); + PaymentMethod pm = new PaymentMethod("PAY-2", method); + PaymentMethod parsed = echoClient.echoPaymentMethod(pm); + assertEquals("PAY-2", parsed.id()); + assertTrue(parsed.method() instanceof PaymentMethodMethod.BankTransferValue); + PaymentMethodMethod.BankTransferValue btv = + (PaymentMethodMethod.BankTransferValue) parsed.method(); + assertEquals("123456789", btv.bankTransfer().accountNumber()); + assertEquals("021000021", btv.bankTransfer().routingNumber()); + } + + @Test + public void testEchoPaymentMethodWallet() { + Wallet w = new Wallet("wallet-42", "Stripe"); + PaymentMethodMethod method = new PaymentMethodMethod.WalletValue(w); + PaymentMethod pm = new PaymentMethod("PAY-3", method); + PaymentMethod parsed = echoClient.echoPaymentMethod(pm); + assertEquals("PAY-3", parsed.id()); + assertTrue(parsed.method() instanceof PaymentMethodMethod.WalletValue); + PaymentMethodMethod.WalletValue wv = (PaymentMethodMethod.WalletValue) parsed.method(); + assertEquals("wallet-42", wv.wallet().walletId()); + assertEquals("Stripe", wv.wallet().provider()); + } + + @Test + public void testEchoNotificationWithEmailTarget() { + Notification notif = + new Notification( + "Hello!", Priority.PRIORITY_HIGH, new NotificationTarget.Email("user@example.com")); + Notification parsed = echoClient.echoNotification(notif); + assertEquals("Hello!", parsed.message()); + assertEquals(Priority.PRIORITY_HIGH, parsed.priority()); + assertTrue(parsed.target() instanceof NotificationTarget.Email); + assertEquals("user@example.com", ((NotificationTarget.Email) parsed.target()).email()); + } + + @Test + public void testEchoNotificationWithPhoneTarget() { + Notification notif = + new Notification( + "Alert", Priority.PRIORITY_CRITICAL, new NotificationTarget.Phone("+1234567890")); + Notification parsed = echoClient.echoNotification(notif); + assertEquals("Alert", parsed.message()); + assertEquals(Priority.PRIORITY_CRITICAL, parsed.priority()); + assertTrue(parsed.target() instanceof NotificationTarget.Phone); + assertEquals("+1234567890", ((NotificationTarget.Phone) parsed.target()).phone()); + } + + @Test + public void testEchoNotificationWithWebhookTarget() { + Notification notif = + new Notification( + "Event", + Priority.PRIORITY_LOW, + new NotificationTarget.WebhookUrl("https://hooks.example.com/abc")); + Notification parsed = echoClient.echoNotification(notif); + assertEquals("Event", parsed.message()); + assertEquals(Priority.PRIORITY_LOW, parsed.priority()); + assertTrue(parsed.target() instanceof NotificationTarget.WebhookUrl); + assertEquals( + "https://hooks.example.com/abc", + ((NotificationTarget.WebhookUrl) parsed.target()).webhookUrl()); + } + + // ---- Collections ---- + + @Test + public void testEchoInventory() { + List productIds = List.of("PROD-1", "PROD-2", "PROD-3"); + Map stockCounts = new LinkedHashMap<>(); + stockCounts.put("PROD-1", 100); + stockCounts.put("PROD-2", 200); + stockCounts.put("PROD-3", 0); + List orders = + List.of( + new Order( + OrderId.valueOf("ORD-1"), + CustomerId.valueOf("CUST-1"), + 1000L, + Instant.ofEpochSecond(1000, 0)), + new Order( + OrderId.valueOf("ORD-2"), + CustomerId.valueOf("CUST-2"), + 2000L, + Instant.ofEpochSecond(2000, 0))); + + Inventory inventory = new Inventory("WH-1", productIds, stockCounts, orders); + Inventory parsed = echoClient.echoInventory(inventory); + assertEquals("WH-1", parsed.warehouseId()); + assertEquals(3, parsed.productIds().size()); + assertEquals("PROD-1", parsed.productIds().get(0)); + assertEquals("PROD-2", parsed.productIds().get(1)); + assertEquals("PROD-3", parsed.productIds().get(2)); + assertEquals(3, parsed.stockCounts().size()); + assertEquals(Integer.valueOf(100), parsed.stockCounts().get("PROD-1")); + assertEquals(Integer.valueOf(200), parsed.stockCounts().get("PROD-2")); + assertEquals(Integer.valueOf(0), parsed.stockCounts().get("PROD-3")); + assertEquals(2, parsed.recentOrders().size()); + assertEquals("ORD-1", parsed.recentOrders().get(0).orderId().unwrap()); + assertEquals("ORD-2", parsed.recentOrders().get(1).orderId().unwrap()); + } + + @Test + public void testEchoInventoryEmptyCollections() { + Inventory inventory = new Inventory("WH-EMPTY", List.of(), Map.of(), List.of()); + Inventory parsed = echoClient.echoInventory(inventory); + assertEquals("WH-EMPTY", parsed.warehouseId()); + assertTrue(parsed.productIds().isEmpty()); + assertTrue(parsed.stockCounts().isEmpty()); + assertTrue(parsed.recentOrders().isEmpty()); + } + + // ---- Well-known types ---- + + @Test + public void testEchoWellKnownTypes() { + WellKnownTypesMessage msg = + new WellKnownTypesMessage( + Instant.ofEpochSecond(1700000000L, 123456789), + Duration.ofSeconds(3600, 500000000), + Optional.of("hello"), + Optional.of(42), + Optional.of(true)); + WellKnownTypesMessage parsed = echoClient.echoWellKnownTypes(msg); + assertEquals(msg.createdAt(), parsed.createdAt()); + assertEquals(msg.ttl(), parsed.ttl()); + assertEquals(Optional.of("hello"), parsed.nullableString()); + assertEquals(Optional.of(42), parsed.nullableInt()); + assertEquals(Optional.of(true), parsed.nullableBool()); + } + + // ---- Wrapper ID types ---- + + @Test + public void testCustomerIdValueOf() { + CustomerId id = CustomerId.valueOf("abc"); + assertEquals("abc", id.unwrap()); + assertEquals("abc", id.toString()); + } + + @Test + public void testOrderIdValueOf() { + OrderId id = OrderId.valueOf("ORD-1"); + assertEquals("ORD-1", id.unwrap()); + assertEquals("ORD-1", id.toString()); + } + + // ---- With methods ---- + + @Test + public void testCustomerWithMethods() { + Customer updated = testCustomer.withName("Jane Doe"); + assertEquals("Jane Doe", updated.name()); + assertEquals(testCustomer.customerId(), updated.customerId()); + assertEquals(testCustomer.email(), updated.email()); + } + + @Test + public void testOrderWithMethods() { + Order order = + new Order( + OrderId.valueOf("ORD-1"), + CustomerId.valueOf("CUST-1"), + 1000L, + Instant.ofEpochSecond(1000, 0)); + Order updated = order.withAmountCents(2000L); + assertEquals(Long.valueOf(2000L), updated.amountCents()); + assertEquals(order.orderId(), updated.orderId()); + } + + // ---- Echo with empty strings ---- + + @Test + public void testEchoCustomerEmptyStrings() { + Customer empty = new Customer(CustomerId.valueOf(""), "", ""); + Customer parsed = echoClient.echoCustomer(empty); + assertEquals("", parsed.customerId().unwrap()); + assertEquals("", parsed.name()); + assertEquals("", parsed.email()); + } +} diff --git a/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/BankTransfer.java b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/BankTransfer.java new file mode 100644 index 0000000000..1fceb9c0ba --- /dev/null +++ b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/BankTransfer.java @@ -0,0 +1,74 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record BankTransfer(String accountNumber, String routingNumber) { + public BankTransfer withAccountNumber(String accountNumber) { + return new BankTransfer(accountNumber, routingNumber); + } + + public BankTransfer withRoutingNumber(String routingNumber) { + return new BankTransfer(accountNumber, routingNumber); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(BankTransfer value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public BankTransfer parse(InputStream stream) { + try { + return BankTransfer.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static BankTransfer parseFrom(CodedInputStream input) throws IOException { + String accountNumber = ""; + String routingNumber = ""; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + accountNumber = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + routingNumber = input.readString(); + } else { + input.skipField(tag); + } + ; + } + ; + return new BankTransfer(accountNumber, routingNumber); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeStringSize(1, this.accountNumber()); + size = size + CodedOutputStream.computeStringSize(2, this.routingNumber()); + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeString(1, this.accountNumber()); + output.writeString(2, this.routingNumber()); + } +} diff --git a/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/ChatMessage.java b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/ChatMessage.java new file mode 100644 index 0000000000..d144c468e5 --- /dev/null +++ b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/ChatMessage.java @@ -0,0 +1,114 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.time.Instant; + +public record ChatMessage(String sender, String content, Instant sentAt) { + public ChatMessage withSender(String sender) { + return new ChatMessage(sender, content, sentAt); + } + + public ChatMessage withContent(String content) { + return new ChatMessage(sender, content, sentAt); + } + + public ChatMessage withSentAt(Instant sentAt) { + return new ChatMessage(sender, content, sentAt); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(ChatMessage value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public ChatMessage parse(InputStream stream) { + try { + return ChatMessage.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static ChatMessage parseFrom(CodedInputStream input) throws IOException { + String sender = ""; + String content = ""; + Instant sentAt = Instant.EPOCH; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + sender = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + content = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 3) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + var _tsSeconds = 0L; + var _tsNanos = 0; + while (!input.isAtEnd()) { + var _tsTag = input.readTag(); + if (WireFormat.getTagFieldNumber(_tsTag) == 1) { + _tsSeconds = input.readInt64(); + } else if (WireFormat.getTagFieldNumber(_tsTag) == 2) { + _tsNanos = input.readInt32(); + } else { + input.skipField(_tsTag); + } + ; + } + ; + sentAt = Instant.ofEpochSecond(_tsSeconds, (long) (_tsNanos)); + input.popLimit(_oldLimit); + ; + } else { + input.skipField(tag); + } + ; + } + ; + return new ChatMessage(sender, content, sentAt); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeStringSize(1, this.sender()); + size = size + CodedOutputStream.computeStringSize(2, this.content()); + size = + size + + CodedOutputStream.computeTagSize(3) + + CodedOutputStream.computeUInt32SizeNoTag( + CodedOutputStream.computeInt64Size(1, this.sentAt().getEpochSecond()) + + CodedOutputStream.computeInt32Size(2, this.sentAt().getNano())) + + CodedOutputStream.computeInt64Size(1, this.sentAt().getEpochSecond()) + + CodedOutputStream.computeInt32Size(2, this.sentAt().getNano()); + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeString(1, this.sender()); + output.writeString(2, this.content()); + output.writeTag(3, 2); + output.writeUInt32NoTag( + CodedOutputStream.computeInt64Size(1, this.sentAt().getEpochSecond()) + + CodedOutputStream.computeInt32Size(2, this.sentAt().getNano())); + output.writeInt64(1, this.sentAt().getEpochSecond()); + output.writeInt32(2, this.sentAt().getNano()); + } +} diff --git a/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/CreateOrderRequest.java b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/CreateOrderRequest.java new file mode 100644 index 0000000000..2682d832d3 --- /dev/null +++ b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/CreateOrderRequest.java @@ -0,0 +1,80 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record CreateOrderRequest(Order order) { + public CreateOrderRequest withOrder(Order order) { + return new CreateOrderRequest(order); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(CreateOrderRequest value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public CreateOrderRequest parse(InputStream stream) { + try { + return CreateOrderRequest.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static CreateOrderRequest parseFrom(CodedInputStream input) throws IOException { + Order order = null; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + order = Order.parseFrom(input); + input.popLimit(_oldLimit); + ; + } else { + input.skipField(tag); + } + ; + } + ; + return new CreateOrderRequest(order); + } + + public Integer getSerializedSize() { + Integer size = 0; + if (!this.order().equals(null)) { + size = + size + + CodedOutputStream.computeTagSize(1) + + CodedOutputStream.computeUInt32SizeNoTag(this.order().getSerializedSize()) + + this.order().getSerializedSize(); + } + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + if (!this.order().equals(null)) { + output.writeTag(1, 2); + output.writeUInt32NoTag(this.order().getSerializedSize()); + this.order().writeTo(output); + ; + } + } +} diff --git a/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/CreateOrderResponse.java b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/CreateOrderResponse.java new file mode 100644 index 0000000000..405cc5b05f --- /dev/null +++ b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/CreateOrderResponse.java @@ -0,0 +1,74 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record CreateOrderResponse(String orderId, OrderStatus status) { + public CreateOrderResponse withOrderId(String orderId) { + return new CreateOrderResponse(orderId, status); + } + + public CreateOrderResponse withStatus(OrderStatus status) { + return new CreateOrderResponse(orderId, status); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(CreateOrderResponse value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public CreateOrderResponse parse(InputStream stream) { + try { + return CreateOrderResponse.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static CreateOrderResponse parseFrom(CodedInputStream input) throws IOException { + String orderId = ""; + OrderStatus status = OrderStatus.fromValue(0); + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + orderId = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + status = OrderStatus.fromValue(input.readEnum()); + } else { + input.skipField(tag); + } + ; + } + ; + return new CreateOrderResponse(orderId, status); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeStringSize(1, this.orderId()); + size = size + CodedOutputStream.computeEnumSize(2, this.status().toValue()); + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeString(1, this.orderId()); + output.writeEnum(2, this.status().toValue()); + } +} diff --git a/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/CreditCard.java b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/CreditCard.java new file mode 100644 index 0000000000..9ab67e6743 --- /dev/null +++ b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/CreditCard.java @@ -0,0 +1,83 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record CreditCard(String cardNumber, String expiryDate, String cvv) { + public CreditCard withCardNumber(String cardNumber) { + return new CreditCard(cardNumber, expiryDate, cvv); + } + + public CreditCard withExpiryDate(String expiryDate) { + return new CreditCard(cardNumber, expiryDate, cvv); + } + + public CreditCard withCvv(String cvv) { + return new CreditCard(cardNumber, expiryDate, cvv); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(CreditCard value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public CreditCard parse(InputStream stream) { + try { + return CreditCard.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static CreditCard parseFrom(CodedInputStream input) throws IOException { + String cardNumber = ""; + String expiryDate = ""; + String cvv = ""; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + cardNumber = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + expiryDate = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 3) { + cvv = input.readString(); + } else { + input.skipField(tag); + } + ; + } + ; + return new CreditCard(cardNumber, expiryDate, cvv); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeStringSize(1, this.cardNumber()); + size = size + CodedOutputStream.computeStringSize(2, this.expiryDate()); + size = size + CodedOutputStream.computeStringSize(3, this.cvv()); + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeString(1, this.cardNumber()); + output.writeString(2, this.expiryDate()); + output.writeString(3, this.cvv()); + } +} diff --git a/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/Customer.java b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/Customer.java new file mode 100644 index 0000000000..9b8f1e46aa --- /dev/null +++ b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/Customer.java @@ -0,0 +1,83 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record Customer(CustomerId customerId, String name, String email) { + public Customer withCustomerId(CustomerId customerId) { + return new Customer(customerId, name, email); + } + + public Customer withName(String name) { + return new Customer(customerId, name, email); + } + + public Customer withEmail(String email) { + return new Customer(customerId, name, email); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(Customer value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public Customer parse(InputStream stream) { + try { + return Customer.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static Customer parseFrom(CodedInputStream input) throws IOException { + CustomerId customerId = CustomerId.valueOf(""); + String name = ""; + String email = ""; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + customerId = CustomerId.valueOf(input.readString()); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + name = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 3) { + email = input.readString(); + } else { + input.skipField(tag); + } + ; + } + ; + return new Customer(customerId, name, email); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeStringSize(1, this.customerId().unwrap()); + size = size + CodedOutputStream.computeStringSize(2, this.name()); + size = size + CodedOutputStream.computeStringSize(3, this.email()); + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeString(1, this.customerId().unwrap()); + output.writeString(2, this.name()); + output.writeString(3, this.email()); + } +} diff --git a/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/CustomerId.java b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/CustomerId.java new file mode 100644 index 0000000000..17a1c0b593 --- /dev/null +++ b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/CustomerId.java @@ -0,0 +1,23 @@ +package com.example.grpc; + +/** Wrapper type for typr.grpc.GrpcCodegen$$$Lambda/0x00007fc001118400@51891008 */ +public record CustomerId(String value) { + public CustomerId withValue(String value) { + return new CustomerId(value); + } + + @Override + public java.lang.String toString() { + return value.toString(); + } + + /** Create a CustomerId from a raw value */ + public static CustomerId valueOf(String v) { + return new CustomerId(v); + } + + /** Get the underlying value */ + public String unwrap() { + return this.value(); + } +} diff --git a/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/EchoService.java b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/EchoService.java new file mode 100644 index 0000000000..c88b0ece5c --- /dev/null +++ b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/EchoService.java @@ -0,0 +1,22 @@ +package com.example.grpc; + +/** Clean service interface for EchoService gRPC service */ +public interface EchoService { + ScalarTypes echoScalarTypes(ScalarTypes request); + + Customer echoCustomer(Customer request); + + Order echoOrder(Order request); + + Inventory echoInventory(Inventory request); + + Outer echoOuter(Outer request); + + OptionalFields echoOptionalFields(OptionalFields request); + + WellKnownTypesMessage echoWellKnownTypes(WellKnownTypesMessage request); + + PaymentMethod echoPaymentMethod(PaymentMethod request); + + Notification echoNotification(Notification request); +} diff --git a/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/EchoServiceClient.java b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/EchoServiceClient.java new file mode 100644 index 0000000000..0c66971277 --- /dev/null +++ b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/EchoServiceClient.java @@ -0,0 +1,126 @@ +package com.example.grpc; + +import io.grpc.CallOptions; +import io.grpc.Channel; +import io.grpc.MethodDescriptor; +import io.grpc.MethodDescriptor.MethodType; +import io.grpc.stub.ClientCalls; + +/** gRPC client wrapper for EchoService - wraps Channel with clean types */ +public class EchoServiceClient implements EchoService { + Channel channel; + + public EchoServiceClient(Channel channel) { + this.channel = channel; + } + + public static MethodDescriptor ECHO_CUSTOMER = + MethodDescriptor.newBuilder(Customer.MARSHALLER, Customer.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoCustomer") + .build(); + + public static MethodDescriptor ECHO_INVENTORY = + MethodDescriptor.newBuilder(Inventory.MARSHALLER, Inventory.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoInventory") + .build(); + + public static MethodDescriptor ECHO_NOTIFICATION = + MethodDescriptor.newBuilder(Notification.MARSHALLER, Notification.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoNotification") + .build(); + + public static MethodDescriptor ECHO_OPTIONAL_FIELDS = + MethodDescriptor.newBuilder(OptionalFields.MARSHALLER, OptionalFields.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoOptionalFields") + .build(); + + public static MethodDescriptor ECHO_ORDER = + MethodDescriptor.newBuilder(Order.MARSHALLER, Order.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoOrder") + .build(); + + public static MethodDescriptor ECHO_OUTER = + MethodDescriptor.newBuilder(Outer.MARSHALLER, Outer.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoOuter") + .build(); + + public static MethodDescriptor ECHO_PAYMENT_METHOD = + MethodDescriptor.newBuilder(PaymentMethod.MARSHALLER, PaymentMethod.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoPaymentMethod") + .build(); + + public static MethodDescriptor ECHO_SCALAR_TYPES = + MethodDescriptor.newBuilder(ScalarTypes.MARSHALLER, ScalarTypes.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoScalarTypes") + .build(); + + public static MethodDescriptor + ECHO_WELL_KNOWN_TYPES = + MethodDescriptor.newBuilder( + WellKnownTypesMessage.MARSHALLER, WellKnownTypesMessage.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoWellKnownTypes") + .build(); + + @Override + public ScalarTypes echoScalarTypes(ScalarTypes request) { + return ClientCalls.blockingUnaryCall( + channel, EchoServiceClient.ECHO_SCALAR_TYPES, CallOptions.DEFAULT, request); + } + + @Override + public Customer echoCustomer(Customer request) { + return ClientCalls.blockingUnaryCall( + channel, EchoServiceClient.ECHO_CUSTOMER, CallOptions.DEFAULT, request); + } + + @Override + public Order echoOrder(Order request) { + return ClientCalls.blockingUnaryCall( + channel, EchoServiceClient.ECHO_ORDER, CallOptions.DEFAULT, request); + } + + @Override + public Inventory echoInventory(Inventory request) { + return ClientCalls.blockingUnaryCall( + channel, EchoServiceClient.ECHO_INVENTORY, CallOptions.DEFAULT, request); + } + + @Override + public Outer echoOuter(Outer request) { + return ClientCalls.blockingUnaryCall( + channel, EchoServiceClient.ECHO_OUTER, CallOptions.DEFAULT, request); + } + + @Override + public OptionalFields echoOptionalFields(OptionalFields request) { + return ClientCalls.blockingUnaryCall( + channel, EchoServiceClient.ECHO_OPTIONAL_FIELDS, CallOptions.DEFAULT, request); + } + + @Override + public WellKnownTypesMessage echoWellKnownTypes(WellKnownTypesMessage request) { + return ClientCalls.blockingUnaryCall( + channel, EchoServiceClient.ECHO_WELL_KNOWN_TYPES, CallOptions.DEFAULT, request); + } + + @Override + public PaymentMethod echoPaymentMethod(PaymentMethod request) { + return ClientCalls.blockingUnaryCall( + channel, EchoServiceClient.ECHO_PAYMENT_METHOD, CallOptions.DEFAULT, request); + } + + @Override + public Notification echoNotification(Notification request) { + return ClientCalls.blockingUnaryCall( + channel, EchoServiceClient.ECHO_NOTIFICATION, CallOptions.DEFAULT, request); + } +} diff --git a/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/EchoServiceServer.java b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/EchoServiceServer.java new file mode 100644 index 0000000000..f983389d44 --- /dev/null +++ b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/EchoServiceServer.java @@ -0,0 +1,145 @@ +package com.example.grpc; + +import io.grpc.BindableService; +import io.grpc.MethodDescriptor; +import io.grpc.MethodDescriptor.MethodType; +import io.grpc.ServerServiceDefinition; +import io.grpc.stub.ServerCalls; +import org.springframework.grpc.server.service.GrpcService; +import org.springframework.stereotype.Service; + +/** gRPC server adapter for EchoService - delegates to clean service interface */ +@GrpcService +@Service +public class EchoServiceServer implements BindableService { + EchoService delegate; + + public EchoServiceServer(EchoService delegate) { + this.delegate = delegate; + } + + public static MethodDescriptor ECHO_CUSTOMER = + MethodDescriptor.newBuilder(Customer.MARSHALLER, Customer.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoCustomer") + .build(); + + public static MethodDescriptor ECHO_INVENTORY = + MethodDescriptor.newBuilder(Inventory.MARSHALLER, Inventory.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoInventory") + .build(); + + public static MethodDescriptor ECHO_NOTIFICATION = + MethodDescriptor.newBuilder(Notification.MARSHALLER, Notification.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoNotification") + .build(); + + public static MethodDescriptor ECHO_OPTIONAL_FIELDS = + MethodDescriptor.newBuilder(OptionalFields.MARSHALLER, OptionalFields.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoOptionalFields") + .build(); + + public static MethodDescriptor ECHO_ORDER = + MethodDescriptor.newBuilder(Order.MARSHALLER, Order.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoOrder") + .build(); + + public static MethodDescriptor ECHO_OUTER = + MethodDescriptor.newBuilder(Outer.MARSHALLER, Outer.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoOuter") + .build(); + + public static MethodDescriptor ECHO_PAYMENT_METHOD = + MethodDescriptor.newBuilder(PaymentMethod.MARSHALLER, PaymentMethod.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoPaymentMethod") + .build(); + + public static MethodDescriptor ECHO_SCALAR_TYPES = + MethodDescriptor.newBuilder(ScalarTypes.MARSHALLER, ScalarTypes.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoScalarTypes") + .build(); + + public static MethodDescriptor + ECHO_WELL_KNOWN_TYPES = + MethodDescriptor.newBuilder( + WellKnownTypesMessage.MARSHALLER, WellKnownTypesMessage.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoWellKnownTypes") + .build(); + + @Override + public ServerServiceDefinition bindService() { + return ServerServiceDefinition.builder("testgrpc.EchoService") + .addMethod( + EchoServiceServer.ECHO_SCALAR_TYPES, + ServerCalls.asyncUnaryCall( + (request, responseObserver) -> { + responseObserver.onNext(delegate.echoScalarTypes(request)); + responseObserver.onCompleted(); + })) + .addMethod( + EchoServiceServer.ECHO_CUSTOMER, + ServerCalls.asyncUnaryCall( + (request, responseObserver) -> { + responseObserver.onNext(delegate.echoCustomer(request)); + responseObserver.onCompleted(); + })) + .addMethod( + EchoServiceServer.ECHO_ORDER, + ServerCalls.asyncUnaryCall( + (request, responseObserver) -> { + responseObserver.onNext(delegate.echoOrder(request)); + responseObserver.onCompleted(); + })) + .addMethod( + EchoServiceServer.ECHO_INVENTORY, + ServerCalls.asyncUnaryCall( + (request, responseObserver) -> { + responseObserver.onNext(delegate.echoInventory(request)); + responseObserver.onCompleted(); + })) + .addMethod( + EchoServiceServer.ECHO_OUTER, + ServerCalls.asyncUnaryCall( + (request, responseObserver) -> { + responseObserver.onNext(delegate.echoOuter(request)); + responseObserver.onCompleted(); + })) + .addMethod( + EchoServiceServer.ECHO_OPTIONAL_FIELDS, + ServerCalls.asyncUnaryCall( + (request, responseObserver) -> { + responseObserver.onNext(delegate.echoOptionalFields(request)); + responseObserver.onCompleted(); + })) + .addMethod( + EchoServiceServer.ECHO_WELL_KNOWN_TYPES, + ServerCalls.asyncUnaryCall( + (request, responseObserver) -> { + responseObserver.onNext(delegate.echoWellKnownTypes(request)); + responseObserver.onCompleted(); + })) + .addMethod( + EchoServiceServer.ECHO_PAYMENT_METHOD, + ServerCalls.asyncUnaryCall( + (request, responseObserver) -> { + responseObserver.onNext(delegate.echoPaymentMethod(request)); + responseObserver.onCompleted(); + })) + .addMethod( + EchoServiceServer.ECHO_NOTIFICATION, + ServerCalls.asyncUnaryCall( + (request, responseObserver) -> { + responseObserver.onNext(delegate.echoNotification(request)); + responseObserver.onCompleted(); + })) + .build(); + } +} diff --git a/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/GetCustomerRequest.java b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/GetCustomerRequest.java new file mode 100644 index 0000000000..5d30c0ed46 --- /dev/null +++ b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/GetCustomerRequest.java @@ -0,0 +1,65 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record GetCustomerRequest(String customerId) { + public GetCustomerRequest withCustomerId(String customerId) { + return new GetCustomerRequest(customerId); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(GetCustomerRequest value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public GetCustomerRequest parse(InputStream stream) { + try { + return GetCustomerRequest.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static GetCustomerRequest parseFrom(CodedInputStream input) throws IOException { + String customerId = ""; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + customerId = input.readString(); + } else { + input.skipField(tag); + } + ; + } + ; + return new GetCustomerRequest(customerId); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeStringSize(1, this.customerId()); + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeString(1, this.customerId()); + } +} diff --git a/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/GetCustomerResponse.java b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/GetCustomerResponse.java new file mode 100644 index 0000000000..3a21596211 --- /dev/null +++ b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/GetCustomerResponse.java @@ -0,0 +1,80 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record GetCustomerResponse(Customer customer) { + public GetCustomerResponse withCustomer(Customer customer) { + return new GetCustomerResponse(customer); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(GetCustomerResponse value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public GetCustomerResponse parse(InputStream stream) { + try { + return GetCustomerResponse.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static GetCustomerResponse parseFrom(CodedInputStream input) throws IOException { + Customer customer = null; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + customer = Customer.parseFrom(input); + input.popLimit(_oldLimit); + ; + } else { + input.skipField(tag); + } + ; + } + ; + return new GetCustomerResponse(customer); + } + + public Integer getSerializedSize() { + Integer size = 0; + if (!this.customer().equals(null)) { + size = + size + + CodedOutputStream.computeTagSize(1) + + CodedOutputStream.computeUInt32SizeNoTag(this.customer().getSerializedSize()) + + this.customer().getSerializedSize(); + } + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + if (!this.customer().equals(null)) { + output.writeTag(1, 2); + output.writeUInt32NoTag(this.customer().getSerializedSize()); + this.customer().writeTo(output); + ; + } + } +} diff --git a/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/Inner.java b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/Inner.java new file mode 100644 index 0000000000..4b45f35f6d --- /dev/null +++ b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/Inner.java @@ -0,0 +1,74 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record Inner(Integer value, String description) { + public Inner withValue(Integer value) { + return new Inner(value, description); + } + + public Inner withDescription(String description) { + return new Inner(value, description); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(Inner value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public Inner parse(InputStream stream) { + try { + return Inner.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static Inner parseFrom(CodedInputStream input) throws IOException { + Integer value = 0; + String description = ""; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + value = input.readInt32(); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + description = input.readString(); + } else { + input.skipField(tag); + } + ; + } + ; + return new Inner(value, description); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeInt32Size(1, this.value()); + size = size + CodedOutputStream.computeStringSize(2, this.description()); + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeInt32(1, this.value()); + output.writeString(2, this.description()); + } +} diff --git a/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/Inventory.java b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/Inventory.java new file mode 100644 index 0000000000..0e8b63bcc4 --- /dev/null +++ b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/Inventory.java @@ -0,0 +1,159 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +public record Inventory( + String warehouseId, + List productIds, + Map stockCounts, + List recentOrders) { + public Inventory withWarehouseId(String warehouseId) { + return new Inventory(warehouseId, productIds, stockCounts, recentOrders); + } + + public Inventory withProductIds(List productIds) { + return new Inventory(warehouseId, productIds, stockCounts, recentOrders); + } + + public Inventory withStockCounts(Map stockCounts) { + return new Inventory(warehouseId, productIds, stockCounts, recentOrders); + } + + public Inventory withRecentOrders(List recentOrders) { + return new Inventory(warehouseId, productIds, stockCounts, recentOrders); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(Inventory value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public Inventory parse(InputStream stream) { + try { + return Inventory.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static Inventory parseFrom(CodedInputStream input) throws IOException { + String warehouseId = ""; + ArrayList productIds = new ArrayList<>(); + HashMap stockCounts = new HashMap(); + ArrayList recentOrders = new ArrayList<>(); + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + warehouseId = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + productIds.add(input.readString()); + } else if (WireFormat.getTagFieldNumber(tag) == 3) { + var length = input.readRawVarint32(); + var oldLimit = input.pushLimit(length); + var mapKey = ""; + var mapValue = 0; + while (!input.isAtEnd()) { + var entryTag = input.readTag(); + if (WireFormat.getTagFieldNumber(entryTag) == 1) { + mapKey = input.readString(); + } else if (WireFormat.getTagFieldNumber(entryTag) == 2) { + mapValue = input.readInt32(); + } else { + input.skipField(entryTag); + } + ; + } + ; + input.popLimit(oldLimit); + stockCounts.put(mapKey, mapValue); + ; + } else if (WireFormat.getTagFieldNumber(tag) == 4) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + recentOrders.add(Order.parseFrom(input)); + input.popLimit(_oldLimit); + ; + } else { + input.skipField(tag); + } + ; + } + ; + return new Inventory(warehouseId, productIds, stockCounts, recentOrders); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeStringSize(1, this.warehouseId()); + for (String elem : this.productIds()) { + size = size + CodedOutputStream.computeStringSize(2, elem); + } + ; + for (Entry entry : this.stockCounts().entrySet()) { + size = + size + + CodedOutputStream.computeTagSize(3) + + CodedOutputStream.computeUInt32SizeNoTag( + CodedOutputStream.computeStringSize(1, entry.getKey()) + + CodedOutputStream.computeInt32Size(2, entry.getValue())) + + CodedOutputStream.computeStringSize(1, entry.getKey()) + + CodedOutputStream.computeInt32Size(2, entry.getValue()); + } + ; + for (Order elem : this.recentOrders()) { + size = + size + + CodedOutputStream.computeTagSize(4) + + CodedOutputStream.computeUInt32SizeNoTag(elem.getSerializedSize()) + + elem.getSerializedSize(); + } + ; + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeString(1, this.warehouseId()); + for (String elem : this.productIds()) { + output.writeString(2, elem); + } + ; + for (Entry entry : this.stockCounts().entrySet()) { + output.writeTag(3, 2); + output.writeUInt32NoTag( + CodedOutputStream.computeStringSize(1, entry.getKey()) + + CodedOutputStream.computeInt32Size(2, entry.getValue())); + output.writeString(1, entry.getKey()); + output.writeInt32(2, entry.getValue()); + } + ; + for (Order elem : this.recentOrders()) { + output.writeTag(4, 2); + output.writeUInt32NoTag(elem.getSerializedSize()); + elem.writeTo(output); + } + ; + } +} diff --git a/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/ListOrdersRequest.java b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/ListOrdersRequest.java new file mode 100644 index 0000000000..3a12489bec --- /dev/null +++ b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/ListOrdersRequest.java @@ -0,0 +1,74 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record ListOrdersRequest(String customerId, Integer pageSize) { + public ListOrdersRequest withCustomerId(String customerId) { + return new ListOrdersRequest(customerId, pageSize); + } + + public ListOrdersRequest withPageSize(Integer pageSize) { + return new ListOrdersRequest(customerId, pageSize); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(ListOrdersRequest value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public ListOrdersRequest parse(InputStream stream) { + try { + return ListOrdersRequest.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static ListOrdersRequest parseFrom(CodedInputStream input) throws IOException { + String customerId = ""; + Integer pageSize = 0; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + customerId = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + pageSize = input.readInt32(); + } else { + input.skipField(tag); + } + ; + } + ; + return new ListOrdersRequest(customerId, pageSize); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeStringSize(1, this.customerId()); + size = size + CodedOutputStream.computeInt32Size(2, this.pageSize()); + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeString(1, this.customerId()); + output.writeInt32(2, this.pageSize()); + } +} diff --git a/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/Notification.java b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/Notification.java new file mode 100644 index 0000000000..908f7b7844 --- /dev/null +++ b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/Notification.java @@ -0,0 +1,102 @@ +package com.example.grpc; + +import com.example.grpc.NotificationTarget.Email; +import com.example.grpc.NotificationTarget.Phone; +import com.example.grpc.NotificationTarget.WebhookUrl; +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record Notification(String message, Priority priority, NotificationTarget target) { + public Notification withMessage(String message) { + return new Notification(message, priority, target); + } + + public Notification withPriority(Priority priority) { + return new Notification(message, priority, target); + } + + public Notification withTarget(NotificationTarget target) { + return new Notification(message, priority, target); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(Notification value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public Notification parse(InputStream stream) { + try { + return Notification.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static Notification parseFrom(CodedInputStream input) throws IOException { + String message = ""; + Priority priority = Priority.fromValue(0); + NotificationTarget target = null; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + message = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + priority = Priority.fromValue(input.readEnum()); + } else if (WireFormat.getTagFieldNumber(tag) == 3) { + target = new Email(input.readString()); + } else if (WireFormat.getTagFieldNumber(tag) == 4) { + target = new Phone(input.readString()); + } else if (WireFormat.getTagFieldNumber(tag) == 5) { + target = new WebhookUrl(input.readString()); + } else { + input.skipField(tag); + } + ; + } + ; + return new Notification(message, priority, target); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeStringSize(1, this.message()); + size = size + CodedOutputStream.computeEnumSize(2, this.priority().toValue()); + switch (this.target()) { + case null -> {} + case Email c -> size = size + CodedOutputStream.computeStringSize(3, c.email()); + case Phone c -> size = size + CodedOutputStream.computeStringSize(4, c.phone()); + case WebhookUrl c -> size = size + CodedOutputStream.computeStringSize(5, c.webhookUrl()); + } + ; + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeString(1, this.message()); + output.writeEnum(2, this.priority().toValue()); + switch (this.target()) { + case null -> {} + case Email c -> output.writeString(3, c.email()); + case Phone c -> output.writeString(4, c.phone()); + case WebhookUrl c -> output.writeString(5, c.webhookUrl()); + } + ; + } +} diff --git a/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/NotificationTarget.java b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/NotificationTarget.java new file mode 100644 index 0000000000..f95bbe8b48 --- /dev/null +++ b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/NotificationTarget.java @@ -0,0 +1,23 @@ +package com.example.grpc; + +/** OneOf type for target */ +public sealed interface NotificationTarget + permits NotificationTarget.Email, NotificationTarget.Phone, NotificationTarget.WebhookUrl { + record Email(String email) implements NotificationTarget { + public Email withEmail(String email) { + return new Email(email); + } + } + + record Phone(String phone) implements NotificationTarget { + public Phone withPhone(String phone) { + return new Phone(phone); + } + } + + record WebhookUrl(String webhookUrl) implements NotificationTarget { + public WebhookUrl withWebhookUrl(String webhookUrl) { + return new WebhookUrl(webhookUrl); + } + } +} diff --git a/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/OptionalFields.java b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/OptionalFields.java new file mode 100644 index 0000000000..534c31a211 --- /dev/null +++ b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/OptionalFields.java @@ -0,0 +1,119 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Optional; + +public record OptionalFields( + Optional name, Optional age, Optional customer) { + public OptionalFields withName(Optional name) { + return new OptionalFields(name, age, customer); + } + + public OptionalFields withAge(Optional age) { + return new OptionalFields(name, age, customer); + } + + public OptionalFields withCustomer(Optional customer) { + return new OptionalFields(name, age, customer); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(OptionalFields value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public OptionalFields parse(InputStream stream) { + try { + return OptionalFields.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static OptionalFields parseFrom(CodedInputStream input) throws IOException { + Optional name = Optional.empty(); + Optional age = Optional.empty(); + Optional customer = Optional.empty(); + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + name = Optional.of(input.readString()); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + age = Optional.of(input.readInt32()); + } else if (WireFormat.getTagFieldNumber(tag) == 3) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + customer = Optional.of(Customer.parseFrom(input)); + input.popLimit(_oldLimit); + ; + } else { + input.skipField(tag); + } + ; + } + ; + return new OptionalFields(name, age, customer); + } + + public Integer getSerializedSize() { + Integer size = 0; + if (this.name().isPresent()) { + var v = this.name().get(); + size = size + CodedOutputStream.computeStringSize(1, v); + ; + } + if (this.age().isPresent()) { + var v = this.age().get(); + size = size + CodedOutputStream.computeInt32Size(2, v); + ; + } + if (this.customer().isPresent()) { + var v = this.customer().get(); + size = + size + + CodedOutputStream.computeTagSize(3) + + CodedOutputStream.computeUInt32SizeNoTag(v.getSerializedSize()) + + v.getSerializedSize(); + ; + } + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + if (this.name().isPresent()) { + var v = this.name().get(); + output.writeString(1, v); + ; + } + if (this.age().isPresent()) { + var v = this.age().get(); + output.writeInt32(2, v); + ; + } + if (this.customer().isPresent()) { + var v = this.customer().get(); + output.writeTag(3, 2); + output.writeUInt32NoTag(v.getSerializedSize()); + v.writeTo(output); + ; + } + } +} diff --git a/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/Order.java b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/Order.java new file mode 100644 index 0000000000..a1dd05fa0b --- /dev/null +++ b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/Order.java @@ -0,0 +1,123 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.time.Instant; + +public record Order(OrderId orderId, CustomerId customerId, Long amountCents, Instant createdAt) { + public Order withOrderId(OrderId orderId) { + return new Order(orderId, customerId, amountCents, createdAt); + } + + public Order withCustomerId(CustomerId customerId) { + return new Order(orderId, customerId, amountCents, createdAt); + } + + public Order withAmountCents(Long amountCents) { + return new Order(orderId, customerId, amountCents, createdAt); + } + + public Order withCreatedAt(Instant createdAt) { + return new Order(orderId, customerId, amountCents, createdAt); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(Order value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public Order parse(InputStream stream) { + try { + return Order.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static Order parseFrom(CodedInputStream input) throws IOException { + OrderId orderId = OrderId.valueOf(""); + CustomerId customerId = CustomerId.valueOf(""); + Long amountCents = 0L; + Instant createdAt = Instant.EPOCH; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + orderId = OrderId.valueOf(input.readString()); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + customerId = CustomerId.valueOf(input.readString()); + } else if (WireFormat.getTagFieldNumber(tag) == 3) { + amountCents = input.readInt64(); + } else if (WireFormat.getTagFieldNumber(tag) == 4) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + var _tsSeconds = 0L; + var _tsNanos = 0; + while (!input.isAtEnd()) { + var _tsTag = input.readTag(); + if (WireFormat.getTagFieldNumber(_tsTag) == 1) { + _tsSeconds = input.readInt64(); + } else if (WireFormat.getTagFieldNumber(_tsTag) == 2) { + _tsNanos = input.readInt32(); + } else { + input.skipField(_tsTag); + } + ; + } + ; + createdAt = Instant.ofEpochSecond(_tsSeconds, (long) (_tsNanos)); + input.popLimit(_oldLimit); + ; + } else { + input.skipField(tag); + } + ; + } + ; + return new Order(orderId, customerId, amountCents, createdAt); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeStringSize(1, this.orderId().unwrap()); + size = size + CodedOutputStream.computeStringSize(2, this.customerId().unwrap()); + size = size + CodedOutputStream.computeInt64Size(3, this.amountCents()); + size = + size + + CodedOutputStream.computeTagSize(4) + + CodedOutputStream.computeUInt32SizeNoTag( + CodedOutputStream.computeInt64Size(1, this.createdAt().getEpochSecond()) + + CodedOutputStream.computeInt32Size(2, this.createdAt().getNano())) + + CodedOutputStream.computeInt64Size(1, this.createdAt().getEpochSecond()) + + CodedOutputStream.computeInt32Size(2, this.createdAt().getNano()); + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeString(1, this.orderId().unwrap()); + output.writeString(2, this.customerId().unwrap()); + output.writeInt64(3, this.amountCents()); + output.writeTag(4, 2); + output.writeUInt32NoTag( + CodedOutputStream.computeInt64Size(1, this.createdAt().getEpochSecond()) + + CodedOutputStream.computeInt32Size(2, this.createdAt().getNano())); + output.writeInt64(1, this.createdAt().getEpochSecond()); + output.writeInt32(2, this.createdAt().getNano()); + } +} diff --git a/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/OrderId.java b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/OrderId.java new file mode 100644 index 0000000000..ddefae63b2 --- /dev/null +++ b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/OrderId.java @@ -0,0 +1,23 @@ +package com.example.grpc; + +/** Wrapper type for typr.grpc.GrpcCodegen$$$Lambda/0x00007fc001118400@2f953efd */ +public record OrderId(String value) { + public OrderId withValue(String value) { + return new OrderId(value); + } + + @Override + public java.lang.String toString() { + return value.toString(); + } + + /** Create a OrderId from a raw value */ + public static OrderId valueOf(String v) { + return new OrderId(v); + } + + /** Get the underlying value */ + public String unwrap() { + return this.value(); + } +} diff --git a/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/OrderService.java b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/OrderService.java new file mode 100644 index 0000000000..743491970c --- /dev/null +++ b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/OrderService.java @@ -0,0 +1,16 @@ +package com.example.grpc; + +import java.util.Iterator; + +/** Clean service interface for OrderService gRPC service */ +public interface OrderService { + GetCustomerResponse getCustomer(GetCustomerRequest request); + + CreateOrderResponse createOrder(CreateOrderRequest request); + + Iterator listOrders(ListOrdersRequest request); + + OrderSummary submitOrders(Iterator requests); + + Iterator chat(Iterator requests); +} diff --git a/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/OrderServiceClient.java b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/OrderServiceClient.java new file mode 100644 index 0000000000..4ee6085081 --- /dev/null +++ b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/OrderServiceClient.java @@ -0,0 +1,76 @@ +package com.example.grpc; + +import io.grpc.CallOptions; +import io.grpc.Channel; +import io.grpc.MethodDescriptor; +import io.grpc.MethodDescriptor.MethodType; +import io.grpc.stub.ClientCalls; +import java.util.Iterator; + +/** gRPC client wrapper for OrderService - wraps Channel with clean types */ +public class OrderServiceClient implements OrderService { + Channel channel; + + public OrderServiceClient(Channel channel) { + this.channel = channel; + } + + public static MethodDescriptor CHAT = + MethodDescriptor.newBuilder(ChatMessage.MARSHALLER, ChatMessage.MARSHALLER) + .setType(MethodType.BIDI_STREAMING) + .setFullMethodName("testgrpc.OrderService/Chat") + .build(); + + public static MethodDescriptor CREATE_ORDER = + MethodDescriptor.newBuilder(CreateOrderRequest.MARSHALLER, CreateOrderResponse.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.OrderService/CreateOrder") + .build(); + + public static MethodDescriptor GET_CUSTOMER = + MethodDescriptor.newBuilder(GetCustomerRequest.MARSHALLER, GetCustomerResponse.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.OrderService/GetCustomer") + .build(); + + public static MethodDescriptor LIST_ORDERS = + MethodDescriptor.newBuilder(ListOrdersRequest.MARSHALLER, OrderUpdate.MARSHALLER) + .setType(MethodType.SERVER_STREAMING) + .setFullMethodName("testgrpc.OrderService/ListOrders") + .build(); + + public static MethodDescriptor SUBMIT_ORDERS = + MethodDescriptor.newBuilder(CreateOrderRequest.MARSHALLER, OrderSummary.MARSHALLER) + .setType(MethodType.CLIENT_STREAMING) + .setFullMethodName("testgrpc.OrderService/SubmitOrders") + .build(); + + @Override + public GetCustomerResponse getCustomer(GetCustomerRequest request) { + return ClientCalls.blockingUnaryCall( + channel, OrderServiceClient.GET_CUSTOMER, CallOptions.DEFAULT, request); + } + + @Override + public CreateOrderResponse createOrder(CreateOrderRequest request) { + return ClientCalls.blockingUnaryCall( + channel, OrderServiceClient.CREATE_ORDER, CallOptions.DEFAULT, request); + } + + @Override + public Iterator listOrders(ListOrdersRequest request) { + return ClientCalls.blockingServerStreamingCall( + channel, OrderServiceClient.LIST_ORDERS, CallOptions.DEFAULT, request); + } + + @Override + public OrderSummary submitOrders(Iterator requests) { + throw new UnsupportedOperationException( + "Client streaming not yet implemented in client wrapper"); + } + + @Override + public Iterator chat(Iterator requests) { + throw new UnsupportedOperationException("Bidi streaming not yet implemented in client wrapper"); + } +} diff --git a/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/OrderServiceServer.java b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/OrderServiceServer.java new file mode 100644 index 0000000000..d69703b2fc --- /dev/null +++ b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/OrderServiceServer.java @@ -0,0 +1,95 @@ +package com.example.grpc; + +import io.grpc.BindableService; +import io.grpc.MethodDescriptor; +import io.grpc.MethodDescriptor.MethodType; +import io.grpc.ServerServiceDefinition; +import io.grpc.stub.ServerCalls; +import org.springframework.grpc.server.service.GrpcService; +import org.springframework.stereotype.Service; + +/** gRPC server adapter for OrderService - delegates to clean service interface */ +@GrpcService +@Service +public class OrderServiceServer implements BindableService { + OrderService delegate; + + public OrderServiceServer(OrderService delegate) { + this.delegate = delegate; + } + + public static MethodDescriptor CHAT = + MethodDescriptor.newBuilder(ChatMessage.MARSHALLER, ChatMessage.MARSHALLER) + .setType(MethodType.BIDI_STREAMING) + .setFullMethodName("testgrpc.OrderService/Chat") + .build(); + + public static MethodDescriptor CREATE_ORDER = + MethodDescriptor.newBuilder(CreateOrderRequest.MARSHALLER, CreateOrderResponse.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.OrderService/CreateOrder") + .build(); + + public static MethodDescriptor GET_CUSTOMER = + MethodDescriptor.newBuilder(GetCustomerRequest.MARSHALLER, GetCustomerResponse.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.OrderService/GetCustomer") + .build(); + + public static MethodDescriptor LIST_ORDERS = + MethodDescriptor.newBuilder(ListOrdersRequest.MARSHALLER, OrderUpdate.MARSHALLER) + .setType(MethodType.SERVER_STREAMING) + .setFullMethodName("testgrpc.OrderService/ListOrders") + .build(); + + public static MethodDescriptor SUBMIT_ORDERS = + MethodDescriptor.newBuilder(CreateOrderRequest.MARSHALLER, OrderSummary.MARSHALLER) + .setType(MethodType.CLIENT_STREAMING) + .setFullMethodName("testgrpc.OrderService/SubmitOrders") + .build(); + + @Override + public ServerServiceDefinition bindService() { + return ServerServiceDefinition.builder("testgrpc.OrderService") + .addMethod( + OrderServiceServer.GET_CUSTOMER, + ServerCalls.asyncUnaryCall( + (request, responseObserver) -> { + responseObserver.onNext(delegate.getCustomer(request)); + responseObserver.onCompleted(); + })) + .addMethod( + OrderServiceServer.CREATE_ORDER, + ServerCalls.asyncUnaryCall( + (request, responseObserver) -> { + responseObserver.onNext(delegate.createOrder(request)); + responseObserver.onCompleted(); + })) + .addMethod( + OrderServiceServer.LIST_ORDERS, + ServerCalls.asyncServerStreamingCall( + (request, responseObserver) -> { + var results = delegate.listOrders(request); + while (results.hasNext()) { + responseObserver.onNext(results.next()); + } + ; + responseObserver.onCompleted(); + })) + .addMethod( + OrderServiceServer.SUBMIT_ORDERS, + ServerCalls.asyncClientStreamingCall( + responseObserver -> { + throw new UnsupportedOperationException( + "Client streaming not yet implemented in server adapter"); + })) + .addMethod( + OrderServiceServer.CHAT, + ServerCalls.asyncBidiStreamingCall( + responseObserver -> { + throw new UnsupportedOperationException( + "Bidi streaming not yet implemented in server adapter"); + })) + .build(); + } +} diff --git a/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/OrderStatus.java b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/OrderStatus.java new file mode 100644 index 0000000000..c0496984dd --- /dev/null +++ b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/OrderStatus.java @@ -0,0 +1,73 @@ +package com.example.grpc; + +public enum OrderStatus { + ORDER_STATUS_UNSPECIFIED("ORDER_STATUS_UNSPECIFIED"), + ORDER_STATUS_PENDING("ORDER_STATUS_PENDING"), + ORDER_STATUS_PROCESSING("ORDER_STATUS_PROCESSING"), + ORDER_STATUS_SHIPPED("ORDER_STATUS_SHIPPED"), + ORDER_STATUS_DELIVERED("ORDER_STATUS_DELIVERED"), + ORDER_STATUS_CANCELLED("ORDER_STATUS_CANCELLED"); + final java.lang.String value; + + public java.lang.String value() { + return value; + } + + OrderStatus(java.lang.String value) { + this.value = value; + } + + public static final java.lang.String Names = + java.util.Arrays.stream(OrderStatus.values()) + .map(x -> x.value) + .collect(java.util.stream.Collectors.joining(", ")); + public static final java.util.Map ByName = + java.util.Arrays.stream(OrderStatus.values()) + .collect(java.util.stream.Collectors.toMap(n -> n.value, n -> n)); + + public Integer toValue() { + if (this.toString().equals("ORDER_STATUS_UNSPECIFIED")) { + return 0; + } else if (this.toString().equals("ORDER_STATUS_PENDING")) { + return 1; + } else if (this.toString().equals("ORDER_STATUS_PROCESSING")) { + return 2; + } else if (this.toString().equals("ORDER_STATUS_SHIPPED")) { + return 3; + } else if (this.toString().equals("ORDER_STATUS_DELIVERED")) { + return 4; + } else if (this.toString().equals("ORDER_STATUS_CANCELLED")) { + return 5; + } else { + return 0; + } + } + + public static OrderStatus fromValue(Integer value) { + if (value == 0) { + return OrderStatus.ORDER_STATUS_UNSPECIFIED; + } else if (value == 1) { + return OrderStatus.ORDER_STATUS_PENDING; + } else if (value == 2) { + return OrderStatus.ORDER_STATUS_PROCESSING; + } else if (value == 3) { + return OrderStatus.ORDER_STATUS_SHIPPED; + } else if (value == 4) { + return OrderStatus.ORDER_STATUS_DELIVERED; + } else if (value == 5) { + return OrderStatus.ORDER_STATUS_CANCELLED; + } else { + throw new IllegalArgumentException("Unknown enum value: " + value); + } + } + ; + + public static OrderStatus force(java.lang.String str) { + if (ByName.containsKey(str)) { + return ByName.get(str); + } else { + throw new RuntimeException( + "'" + str + "' does not match any of the following legal values: " + Names); + } + } +} diff --git a/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/OrderSummary.java b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/OrderSummary.java new file mode 100644 index 0000000000..e2750a4e1d --- /dev/null +++ b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/OrderSummary.java @@ -0,0 +1,74 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record OrderSummary(Integer totalOrders, Long totalAmountCents) { + public OrderSummary withTotalOrders(Integer totalOrders) { + return new OrderSummary(totalOrders, totalAmountCents); + } + + public OrderSummary withTotalAmountCents(Long totalAmountCents) { + return new OrderSummary(totalOrders, totalAmountCents); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(OrderSummary value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public OrderSummary parse(InputStream stream) { + try { + return OrderSummary.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static OrderSummary parseFrom(CodedInputStream input) throws IOException { + Integer totalOrders = 0; + Long totalAmountCents = 0L; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + totalOrders = input.readInt32(); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + totalAmountCents = input.readInt64(); + } else { + input.skipField(tag); + } + ; + } + ; + return new OrderSummary(totalOrders, totalAmountCents); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeInt32Size(1, this.totalOrders()); + size = size + CodedOutputStream.computeInt64Size(2, this.totalAmountCents()); + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeInt32(1, this.totalOrders()); + output.writeInt64(2, this.totalAmountCents()); + } +} diff --git a/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/OrderUpdate.java b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/OrderUpdate.java new file mode 100644 index 0000000000..ed13d01723 --- /dev/null +++ b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/OrderUpdate.java @@ -0,0 +1,114 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.time.Instant; + +public record OrderUpdate(String orderId, OrderStatus status, Instant updatedAt) { + public OrderUpdate withOrderId(String orderId) { + return new OrderUpdate(orderId, status, updatedAt); + } + + public OrderUpdate withStatus(OrderStatus status) { + return new OrderUpdate(orderId, status, updatedAt); + } + + public OrderUpdate withUpdatedAt(Instant updatedAt) { + return new OrderUpdate(orderId, status, updatedAt); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(OrderUpdate value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public OrderUpdate parse(InputStream stream) { + try { + return OrderUpdate.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static OrderUpdate parseFrom(CodedInputStream input) throws IOException { + String orderId = ""; + OrderStatus status = OrderStatus.fromValue(0); + Instant updatedAt = Instant.EPOCH; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + orderId = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + status = OrderStatus.fromValue(input.readEnum()); + } else if (WireFormat.getTagFieldNumber(tag) == 3) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + var _tsSeconds = 0L; + var _tsNanos = 0; + while (!input.isAtEnd()) { + var _tsTag = input.readTag(); + if (WireFormat.getTagFieldNumber(_tsTag) == 1) { + _tsSeconds = input.readInt64(); + } else if (WireFormat.getTagFieldNumber(_tsTag) == 2) { + _tsNanos = input.readInt32(); + } else { + input.skipField(_tsTag); + } + ; + } + ; + updatedAt = Instant.ofEpochSecond(_tsSeconds, (long) (_tsNanos)); + input.popLimit(_oldLimit); + ; + } else { + input.skipField(tag); + } + ; + } + ; + return new OrderUpdate(orderId, status, updatedAt); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeStringSize(1, this.orderId()); + size = size + CodedOutputStream.computeEnumSize(2, this.status().toValue()); + size = + size + + CodedOutputStream.computeTagSize(3) + + CodedOutputStream.computeUInt32SizeNoTag( + CodedOutputStream.computeInt64Size(1, this.updatedAt().getEpochSecond()) + + CodedOutputStream.computeInt32Size(2, this.updatedAt().getNano())) + + CodedOutputStream.computeInt64Size(1, this.updatedAt().getEpochSecond()) + + CodedOutputStream.computeInt32Size(2, this.updatedAt().getNano()); + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeString(1, this.orderId()); + output.writeEnum(2, this.status().toValue()); + output.writeTag(3, 2); + output.writeUInt32NoTag( + CodedOutputStream.computeInt64Size(1, this.updatedAt().getEpochSecond()) + + CodedOutputStream.computeInt32Size(2, this.updatedAt().getNano())); + output.writeInt64(1, this.updatedAt().getEpochSecond()); + output.writeInt32(2, this.updatedAt().getNano()); + } +} diff --git a/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/Outer.java b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/Outer.java new file mode 100644 index 0000000000..e60998c822 --- /dev/null +++ b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/Outer.java @@ -0,0 +1,89 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record Outer(String name, Inner inner) { + public Outer withName(String name) { + return new Outer(name, inner); + } + + public Outer withInner(Inner inner) { + return new Outer(name, inner); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(Outer value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public Outer parse(InputStream stream) { + try { + return Outer.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static Outer parseFrom(CodedInputStream input) throws IOException { + String name = ""; + Inner inner = null; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + name = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + inner = Inner.parseFrom(input); + input.popLimit(_oldLimit); + ; + } else { + input.skipField(tag); + } + ; + } + ; + return new Outer(name, inner); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeStringSize(1, this.name()); + if (!this.inner().equals(null)) { + size = + size + + CodedOutputStream.computeTagSize(2) + + CodedOutputStream.computeUInt32SizeNoTag(this.inner().getSerializedSize()) + + this.inner().getSerializedSize(); + } + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeString(1, this.name()); + if (!this.inner().equals(null)) { + output.writeTag(2, 2); + output.writeUInt32NoTag(this.inner().getSerializedSize()); + this.inner().writeTo(output); + ; + } + } +} diff --git a/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/PaymentMethod.java b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/PaymentMethod.java new file mode 100644 index 0000000000..329bb47f66 --- /dev/null +++ b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/PaymentMethod.java @@ -0,0 +1,132 @@ +package com.example.grpc; + +import com.example.grpc.PaymentMethodMethod.BankTransferValue; +import com.example.grpc.PaymentMethodMethod.CreditCardValue; +import com.example.grpc.PaymentMethodMethod.WalletValue; +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record PaymentMethod(String id, PaymentMethodMethod method) { + public PaymentMethod withId(String id) { + return new PaymentMethod(id, method); + } + + public PaymentMethod withMethod(PaymentMethodMethod method) { + return new PaymentMethod(id, method); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(PaymentMethod value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public PaymentMethod parse(InputStream stream) { + try { + return PaymentMethod.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static PaymentMethod parseFrom(CodedInputStream input) throws IOException { + String id = ""; + PaymentMethodMethod method = null; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + id = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + method = new CreditCardValue(CreditCard.parseFrom(input)); + input.popLimit(_oldLimit); + ; + } else if (WireFormat.getTagFieldNumber(tag) == 3) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + method = new BankTransferValue(BankTransfer.parseFrom(input)); + input.popLimit(_oldLimit); + ; + } else if (WireFormat.getTagFieldNumber(tag) == 4) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + method = new WalletValue(Wallet.parseFrom(input)); + input.popLimit(_oldLimit); + ; + } else { + input.skipField(tag); + } + ; + } + ; + return new PaymentMethod(id, method); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeStringSize(1, this.id()); + switch (this.method()) { + case null -> {} + case CreditCardValue c -> + size = + size + + CodedOutputStream.computeTagSize(2) + + CodedOutputStream.computeUInt32SizeNoTag(c.creditCard().getSerializedSize()) + + c.creditCard().getSerializedSize(); + case BankTransferValue c -> + size = + size + + CodedOutputStream.computeTagSize(3) + + CodedOutputStream.computeUInt32SizeNoTag(c.bankTransfer().getSerializedSize()) + + c.bankTransfer().getSerializedSize(); + case WalletValue c -> + size = + size + + CodedOutputStream.computeTagSize(4) + + CodedOutputStream.computeUInt32SizeNoTag(c.wallet().getSerializedSize()) + + c.wallet().getSerializedSize(); + } + ; + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeString(1, this.id()); + switch (this.method()) { + case null -> {} + case CreditCardValue c -> { + output.writeTag(2, 2); + output.writeUInt32NoTag(c.creditCard().getSerializedSize()); + c.creditCard().writeTo(output); + } + case BankTransferValue c -> { + output.writeTag(3, 2); + output.writeUInt32NoTag(c.bankTransfer().getSerializedSize()); + c.bankTransfer().writeTo(output); + } + case WalletValue c -> { + output.writeTag(4, 2); + output.writeUInt32NoTag(c.wallet().getSerializedSize()); + c.wallet().writeTo(output); + } + } + ; + } +} diff --git a/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/PaymentMethodMethod.java b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/PaymentMethodMethod.java new file mode 100644 index 0000000000..7772f87dae --- /dev/null +++ b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/PaymentMethodMethod.java @@ -0,0 +1,25 @@ +package com.example.grpc; + +/** OneOf type for method */ +public sealed interface PaymentMethodMethod + permits PaymentMethodMethod.CreditCardValue, + PaymentMethodMethod.BankTransferValue, + PaymentMethodMethod.WalletValue { + record BankTransferValue(BankTransfer bankTransfer) implements PaymentMethodMethod { + public BankTransferValue withBankTransfer(BankTransfer bankTransfer) { + return new BankTransferValue(bankTransfer); + } + } + + record CreditCardValue(CreditCard creditCard) implements PaymentMethodMethod { + public CreditCardValue withCreditCard(CreditCard creditCard) { + return new CreditCardValue(creditCard); + } + } + + record WalletValue(Wallet wallet) implements PaymentMethodMethod { + public WalletValue withWallet(Wallet wallet) { + return new WalletValue(wallet); + } + } +} diff --git a/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/Priority.java b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/Priority.java new file mode 100644 index 0000000000..d11a842342 --- /dev/null +++ b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/Priority.java @@ -0,0 +1,68 @@ +package com.example.grpc; + +public enum Priority { + PRIORITY_UNSPECIFIED("PRIORITY_UNSPECIFIED"), + PRIORITY_LOW("PRIORITY_LOW"), + PRIORITY_MEDIUM("PRIORITY_MEDIUM"), + PRIORITY_HIGH("PRIORITY_HIGH"), + PRIORITY_CRITICAL("PRIORITY_CRITICAL"); + final java.lang.String value; + + public java.lang.String value() { + return value; + } + + Priority(java.lang.String value) { + this.value = value; + } + + public static final java.lang.String Names = + java.util.Arrays.stream(Priority.values()) + .map(x -> x.value) + .collect(java.util.stream.Collectors.joining(", ")); + public static final java.util.Map ByName = + java.util.Arrays.stream(Priority.values()) + .collect(java.util.stream.Collectors.toMap(n -> n.value, n -> n)); + + public Integer toValue() { + if (this.toString().equals("PRIORITY_UNSPECIFIED")) { + return 0; + } else if (this.toString().equals("PRIORITY_LOW")) { + return 1; + } else if (this.toString().equals("PRIORITY_MEDIUM")) { + return 2; + } else if (this.toString().equals("PRIORITY_HIGH")) { + return 3; + } else if (this.toString().equals("PRIORITY_CRITICAL")) { + return 4; + } else { + return 0; + } + } + + public static Priority fromValue(Integer value) { + if (value == 0) { + return Priority.PRIORITY_UNSPECIFIED; + } else if (value == 1) { + return Priority.PRIORITY_LOW; + } else if (value == 2) { + return Priority.PRIORITY_MEDIUM; + } else if (value == 3) { + return Priority.PRIORITY_HIGH; + } else if (value == 4) { + return Priority.PRIORITY_CRITICAL; + } else { + throw new IllegalArgumentException("Unknown enum value: " + value); + } + } + ; + + public static Priority force(java.lang.String str) { + if (ByName.containsKey(str)) { + return ByName.get(str); + } else { + throw new RuntimeException( + "'" + str + "' does not match any of the following legal values: " + Names); + } + } +} diff --git a/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/ScalarTypes.java b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/ScalarTypes.java new file mode 100644 index 0000000000..3117c27eda --- /dev/null +++ b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/ScalarTypes.java @@ -0,0 +1,447 @@ +package com.example.grpc; + +import com.google.protobuf.ByteString; +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record ScalarTypes( + Double doubleVal, + Float floatVal, + Integer int32Val, + Long int64Val, + Integer uint32Val, + Long uint64Val, + Integer sint32Val, + Long sint64Val, + Integer fixed32Val, + Long fixed64Val, + Integer sfixed32Val, + Long sfixed64Val, + Boolean boolVal, + String stringVal, + ByteString bytesVal) { + public ScalarTypes withDoubleVal(Double doubleVal) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public ScalarTypes withFloatVal(Float floatVal) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public ScalarTypes withInt32Val(Integer int32Val) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public ScalarTypes withInt64Val(Long int64Val) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public ScalarTypes withUint32Val(Integer uint32Val) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public ScalarTypes withUint64Val(Long uint64Val) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public ScalarTypes withSint32Val(Integer sint32Val) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public ScalarTypes withSint64Val(Long sint64Val) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public ScalarTypes withFixed32Val(Integer fixed32Val) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public ScalarTypes withFixed64Val(Long fixed64Val) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public ScalarTypes withSfixed32Val(Integer sfixed32Val) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public ScalarTypes withSfixed64Val(Long sfixed64Val) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public ScalarTypes withBoolVal(Boolean boolVal) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public ScalarTypes withStringVal(String stringVal) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public ScalarTypes withBytesVal(ByteString bytesVal) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(ScalarTypes value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public ScalarTypes parse(InputStream stream) { + try { + return ScalarTypes.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static ScalarTypes parseFrom(CodedInputStream input) throws IOException { + Double doubleVal = 0.0; + Float floatVal = 0.0f; + Integer int32Val = 0; + Long int64Val = 0L; + Integer uint32Val = 0; + Long uint64Val = 0L; + Integer sint32Val = 0; + Long sint64Val = 0L; + Integer fixed32Val = 0; + Long fixed64Val = 0L; + Integer sfixed32Val = 0; + Long sfixed64Val = 0L; + Boolean boolVal = false; + String stringVal = ""; + ByteString bytesVal = ByteString.EMPTY; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + doubleVal = input.readDouble(); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + floatVal = input.readFloat(); + } else if (WireFormat.getTagFieldNumber(tag) == 3) { + int32Val = input.readInt32(); + } else if (WireFormat.getTagFieldNumber(tag) == 4) { + int64Val = input.readInt64(); + } else if (WireFormat.getTagFieldNumber(tag) == 5) { + uint32Val = input.readUInt32(); + } else if (WireFormat.getTagFieldNumber(tag) == 6) { + uint64Val = input.readUInt64(); + } else if (WireFormat.getTagFieldNumber(tag) == 7) { + sint32Val = input.readSInt32(); + } else if (WireFormat.getTagFieldNumber(tag) == 8) { + sint64Val = input.readSInt64(); + } else if (WireFormat.getTagFieldNumber(tag) == 9) { + fixed32Val = input.readFixed32(); + } else if (WireFormat.getTagFieldNumber(tag) == 10) { + fixed64Val = input.readFixed64(); + } else if (WireFormat.getTagFieldNumber(tag) == 11) { + sfixed32Val = input.readSFixed32(); + } else if (WireFormat.getTagFieldNumber(tag) == 12) { + sfixed64Val = input.readSFixed64(); + } else if (WireFormat.getTagFieldNumber(tag) == 13) { + boolVal = input.readBool(); + } else if (WireFormat.getTagFieldNumber(tag) == 14) { + stringVal = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 15) { + bytesVal = input.readBytes(); + } else { + input.skipField(tag); + } + ; + } + ; + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeDoubleSize(1, this.doubleVal()); + size = size + CodedOutputStream.computeFloatSize(2, this.floatVal()); + size = size + CodedOutputStream.computeInt32Size(3, this.int32Val()); + size = size + CodedOutputStream.computeInt64Size(4, this.int64Val()); + size = size + CodedOutputStream.computeUInt32Size(5, this.uint32Val()); + size = size + CodedOutputStream.computeUInt64Size(6, this.uint64Val()); + size = size + CodedOutputStream.computeSInt32Size(7, this.sint32Val()); + size = size + CodedOutputStream.computeSInt64Size(8, this.sint64Val()); + size = size + CodedOutputStream.computeFixed32Size(9, this.fixed32Val()); + size = size + CodedOutputStream.computeFixed64Size(10, this.fixed64Val()); + size = size + CodedOutputStream.computeSFixed32Size(11, this.sfixed32Val()); + size = size + CodedOutputStream.computeSFixed64Size(12, this.sfixed64Val()); + size = size + CodedOutputStream.computeBoolSize(13, this.boolVal()); + size = size + CodedOutputStream.computeStringSize(14, this.stringVal()); + size = size + CodedOutputStream.computeBytesSize(15, this.bytesVal()); + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeDouble(1, this.doubleVal()); + output.writeFloat(2, this.floatVal()); + output.writeInt32(3, this.int32Val()); + output.writeInt64(4, this.int64Val()); + output.writeUInt32(5, this.uint32Val()); + output.writeUInt64(6, this.uint64Val()); + output.writeSInt32(7, this.sint32Val()); + output.writeSInt64(8, this.sint64Val()); + output.writeFixed32(9, this.fixed32Val()); + output.writeFixed64(10, this.fixed64Val()); + output.writeSFixed32(11, this.sfixed32Val()); + output.writeSFixed64(12, this.sfixed64Val()); + output.writeBool(13, this.boolVal()); + output.writeString(14, this.stringVal()); + output.writeBytes(15, this.bytesVal()); + } +} diff --git a/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/Wallet.java b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/Wallet.java new file mode 100644 index 0000000000..52ce7c24d5 --- /dev/null +++ b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/Wallet.java @@ -0,0 +1,74 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record Wallet(String walletId, String provider) { + public Wallet withWalletId(String walletId) { + return new Wallet(walletId, provider); + } + + public Wallet withProvider(String provider) { + return new Wallet(walletId, provider); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(Wallet value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public Wallet parse(InputStream stream) { + try { + return Wallet.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static Wallet parseFrom(CodedInputStream input) throws IOException { + String walletId = ""; + String provider = ""; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + walletId = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + provider = input.readString(); + } else { + input.skipField(tag); + } + ; + } + ; + return new Wallet(walletId, provider); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeStringSize(1, this.walletId()); + size = size + CodedOutputStream.computeStringSize(2, this.provider()); + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeString(1, this.walletId()); + output.writeString(2, this.provider()); + } +} diff --git a/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/WellKnownTypesMessage.java b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/WellKnownTypesMessage.java new file mode 100644 index 0000000000..f0ee16f7e5 --- /dev/null +++ b/testers/grpc/java-spring/generated-and-checked-in/com/example/grpc/WellKnownTypesMessage.java @@ -0,0 +1,226 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.time.Duration; +import java.time.Instant; +import java.util.Optional; + +public record WellKnownTypesMessage( + Instant createdAt, + Duration ttl, + Optional nullableString, + Optional nullableInt, + Optional nullableBool) { + public WellKnownTypesMessage withCreatedAt(Instant createdAt) { + return new WellKnownTypesMessage(createdAt, ttl, nullableString, nullableInt, nullableBool); + } + + public WellKnownTypesMessage withTtl(Duration ttl) { + return new WellKnownTypesMessage(createdAt, ttl, nullableString, nullableInt, nullableBool); + } + + public WellKnownTypesMessage withNullableString(Optional nullableString) { + return new WellKnownTypesMessage(createdAt, ttl, nullableString, nullableInt, nullableBool); + } + + public WellKnownTypesMessage withNullableInt(Optional nullableInt) { + return new WellKnownTypesMessage(createdAt, ttl, nullableString, nullableInt, nullableBool); + } + + public WellKnownTypesMessage withNullableBool(Optional nullableBool) { + return new WellKnownTypesMessage(createdAt, ttl, nullableString, nullableInt, nullableBool); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(WellKnownTypesMessage value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public WellKnownTypesMessage parse(InputStream stream) { + try { + return WellKnownTypesMessage.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static WellKnownTypesMessage parseFrom(CodedInputStream input) throws IOException { + Instant createdAt = Instant.EPOCH; + Duration ttl = Duration.ZERO; + Optional nullableString = Optional.empty(); + Optional nullableInt = Optional.empty(); + Optional nullableBool = Optional.empty(); + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + var _tsSeconds = 0L; + var _tsNanos = 0; + while (!input.isAtEnd()) { + var _tsTag = input.readTag(); + if (WireFormat.getTagFieldNumber(_tsTag) == 1) { + _tsSeconds = input.readInt64(); + } else if (WireFormat.getTagFieldNumber(_tsTag) == 2) { + _tsNanos = input.readInt32(); + } else { + input.skipField(_tsTag); + } + ; + } + ; + createdAt = Instant.ofEpochSecond(_tsSeconds, (long) (_tsNanos)); + input.popLimit(_oldLimit); + ; + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + var _durSeconds = 0L; + var _durNanos = 0; + while (!input.isAtEnd()) { + var _durTag = input.readTag(); + if (WireFormat.getTagFieldNumber(_durTag) == 1) { + _durSeconds = input.readInt64(); + } else if (WireFormat.getTagFieldNumber(_durTag) == 2) { + _durNanos = input.readInt32(); + } else { + input.skipField(_durTag); + } + ; + } + ; + ttl = Duration.ofSeconds(_durSeconds, (long) (_durNanos)); + input.popLimit(_oldLimit); + ; + } else if (WireFormat.getTagFieldNumber(tag) == 3) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + input.readTag(); + nullableString = Optional.of(input.readString()); + input.popLimit(_oldLimit); + ; + } else if (WireFormat.getTagFieldNumber(tag) == 4) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + input.readTag(); + nullableInt = Optional.of(input.readInt32()); + input.popLimit(_oldLimit); + ; + } else if (WireFormat.getTagFieldNumber(tag) == 5) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + input.readTag(); + nullableBool = Optional.of(input.readBool()); + input.popLimit(_oldLimit); + ; + } else { + input.skipField(tag); + } + ; + } + ; + return new WellKnownTypesMessage(createdAt, ttl, nullableString, nullableInt, nullableBool); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = + size + + CodedOutputStream.computeTagSize(1) + + CodedOutputStream.computeUInt32SizeNoTag( + CodedOutputStream.computeInt64Size(1, this.createdAt().getEpochSecond()) + + CodedOutputStream.computeInt32Size(2, this.createdAt().getNano())) + + CodedOutputStream.computeInt64Size(1, this.createdAt().getEpochSecond()) + + CodedOutputStream.computeInt32Size(2, this.createdAt().getNano()); + size = + size + + CodedOutputStream.computeTagSize(2) + + CodedOutputStream.computeUInt32SizeNoTag( + CodedOutputStream.computeInt64Size(1, this.ttl().getSeconds()) + + CodedOutputStream.computeInt32Size(2, this.ttl().getNano())) + + CodedOutputStream.computeInt64Size(1, this.ttl().getSeconds()) + + CodedOutputStream.computeInt32Size(2, this.ttl().getNano()); + if (this.nullableString().isPresent()) { + var v = this.nullableString().get(); + size = + size + + CodedOutputStream.computeTagSize(3) + + CodedOutputStream.computeUInt32SizeNoTag(CodedOutputStream.computeStringSize(1, v)) + + CodedOutputStream.computeStringSize(1, v); + ; + } + if (this.nullableInt().isPresent()) { + var v = this.nullableInt().get(); + size = + size + + CodedOutputStream.computeTagSize(4) + + CodedOutputStream.computeUInt32SizeNoTag(CodedOutputStream.computeInt32Size(1, v)) + + CodedOutputStream.computeInt32Size(1, v); + ; + } + if (this.nullableBool().isPresent()) { + var v = this.nullableBool().get(); + size = + size + + CodedOutputStream.computeTagSize(5) + + CodedOutputStream.computeUInt32SizeNoTag(CodedOutputStream.computeBoolSize(1, v)) + + CodedOutputStream.computeBoolSize(1, v); + ; + } + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeTag(1, 2); + output.writeUInt32NoTag( + CodedOutputStream.computeInt64Size(1, this.createdAt().getEpochSecond()) + + CodedOutputStream.computeInt32Size(2, this.createdAt().getNano())); + output.writeInt64(1, this.createdAt().getEpochSecond()); + output.writeInt32(2, this.createdAt().getNano()); + output.writeTag(2, 2); + output.writeUInt32NoTag( + CodedOutputStream.computeInt64Size(1, this.ttl().getSeconds()) + + CodedOutputStream.computeInt32Size(2, this.ttl().getNano())); + output.writeInt64(1, this.ttl().getSeconds()); + output.writeInt32(2, this.ttl().getNano()); + if (this.nullableString().isPresent()) { + var v = this.nullableString().get(); + output.writeTag(3, 2); + output.writeUInt32NoTag(CodedOutputStream.computeStringSize(1, v)); + output.writeString(1, v); + ; + } + if (this.nullableInt().isPresent()) { + var v = this.nullableInt().get(); + output.writeTag(4, 2); + output.writeUInt32NoTag(CodedOutputStream.computeInt32Size(1, v)); + output.writeInt32(1, v); + ; + } + if (this.nullableBool().isPresent()) { + var v = this.nullableBool().get(); + output.writeTag(5, 2); + output.writeUInt32NoTag(CodedOutputStream.computeBoolSize(1, v)); + output.writeBool(1, v); + ; + } + } +} diff --git a/testers/grpc/java-spring/src/java/com/example/grpc/GrpcIntegrationTest.java b/testers/grpc/java-spring/src/java/com/example/grpc/GrpcIntegrationTest.java new file mode 100644 index 0000000000..5fb9d58f5f --- /dev/null +++ b/testers/grpc/java-spring/src/java/com/example/grpc/GrpcIntegrationTest.java @@ -0,0 +1,520 @@ +package com.example.grpc; + +import static org.junit.Assert.*; + +import com.google.protobuf.ByteString; +import io.grpc.ManagedChannel; +import io.grpc.Server; +import io.grpc.inprocess.InProcessChannelBuilder; +import io.grpc.inprocess.InProcessServerBuilder; +import java.time.Duration; +import java.time.Instant; +import java.util.*; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class GrpcIntegrationTest { + + private Server server; + private ManagedChannel channel; + private OrderServiceClient orderClient; + private EchoServiceClient echoClient; + + private final Customer testCustomer = + new Customer(CustomerId.valueOf("CUST-123"), "John Doe", "john@example.com"); + + @Before + public void setUp() throws Exception { + String serverName = InProcessServerBuilder.generateName(); + + OrderService orderImpl = + new OrderService() { + @Override + public GetCustomerResponse getCustomer(GetCustomerRequest request) { + return new GetCustomerResponse( + new Customer( + CustomerId.valueOf(request.customerId()), "John Doe", "john@example.com")); + } + + @Override + public CreateOrderResponse createOrder(CreateOrderRequest request) { + return new CreateOrderResponse( + request.order().orderId().unwrap(), OrderStatus.ORDER_STATUS_PENDING); + } + + @Override + public Iterator listOrders(ListOrdersRequest request) { + List updates = new ArrayList<>(); + updates.add( + new OrderUpdate( + "ORD-1", OrderStatus.ORDER_STATUS_PENDING, Instant.ofEpochSecond(1000, 500))); + updates.add( + new OrderUpdate( + "ORD-2", OrderStatus.ORDER_STATUS_SHIPPED, Instant.ofEpochSecond(2000, 1000))); + updates.add( + new OrderUpdate( + "ORD-3", OrderStatus.ORDER_STATUS_DELIVERED, Instant.ofEpochSecond(3000, 0))); + return updates.iterator(); + } + + @Override + public OrderSummary submitOrders(Iterator requests) { + throw new UnsupportedOperationException(); + } + + @Override + public Iterator chat(Iterator requests) { + throw new UnsupportedOperationException(); + } + }; + + EchoService echoImpl = + new EchoService() { + @Override + public ScalarTypes echoScalarTypes(ScalarTypes request) { + return request; + } + + @Override + public Customer echoCustomer(Customer request) { + return request; + } + + @Override + public Order echoOrder(Order request) { + return request; + } + + @Override + public Inventory echoInventory(Inventory request) { + return request; + } + + @Override + public Outer echoOuter(Outer request) { + return request; + } + + @Override + public OptionalFields echoOptionalFields(OptionalFields request) { + return request; + } + + @Override + public WellKnownTypesMessage echoWellKnownTypes(WellKnownTypesMessage request) { + return request; + } + + @Override + public PaymentMethod echoPaymentMethod(PaymentMethod request) { + return request; + } + + @Override + public Notification echoNotification(Notification request) { + return request; + } + }; + + server = + InProcessServerBuilder.forName(serverName) + .directExecutor() + .addService(new OrderServiceServer(orderImpl)) + .addService(new EchoServiceServer(echoImpl)) + .build() + .start(); + + channel = InProcessChannelBuilder.forName(serverName).directExecutor().build(); + orderClient = new OrderServiceClient(channel); + echoClient = new EchoServiceClient(channel); + } + + @After + public void tearDown() throws Exception { + channel.shutdownNow(); + server.shutdownNow(); + } + + // ---- gRPC service tests ---- + + @Test + public void testGetCustomer() { + GetCustomerResponse response = orderClient.getCustomer(new GetCustomerRequest("CUST-123")); + assertNotNull(response); + assertNotNull(response.customer()); + assertEquals("CUST-123", response.customer().customerId().unwrap()); + assertEquals("John Doe", response.customer().name()); + assertEquals("john@example.com", response.customer().email()); + } + + @Test + public void testCreateOrder() { + Order order = + new Order( + OrderId.valueOf("ORD-42"), + CustomerId.valueOf("CUST-1"), + 9999L, + Instant.ofEpochSecond(1700000000L, 123456789)); + + CreateOrderResponse response = orderClient.createOrder(new CreateOrderRequest(order)); + assertNotNull(response); + assertEquals("ORD-42", response.orderId()); + assertEquals(OrderStatus.ORDER_STATUS_PENDING, response.status()); + } + + @Test + public void testListOrders() { + Iterator updates = orderClient.listOrders(new ListOrdersRequest("CUST-123", 10)); + List results = new ArrayList<>(); + updates.forEachRemaining(results::add); + + assertEquals(3, results.size()); + assertEquals("ORD-1", results.get(0).orderId()); + assertEquals(OrderStatus.ORDER_STATUS_PENDING, results.get(0).status()); + assertEquals("ORD-2", results.get(1).orderId()); + assertEquals(OrderStatus.ORDER_STATUS_SHIPPED, results.get(1).status()); + assertEquals("ORD-3", results.get(2).orderId()); + assertEquals(OrderStatus.ORDER_STATUS_DELIVERED, results.get(2).status()); + } + + // ---- Echo round-trip tests ---- + + @Test + public void testEchoCustomer() { + Customer parsed = echoClient.echoCustomer(testCustomer); + assertEquals(testCustomer, parsed); + } + + @Test + public void testEchoOrder() { + Order order = + new Order( + OrderId.valueOf("ORD-1"), + CustomerId.valueOf("CUST-1"), + 5000L, + Instant.ofEpochSecond(1700000000L, 123456789)); + Order parsed = echoClient.echoOrder(order); + assertEquals(order.orderId(), parsed.orderId()); + assertEquals(order.customerId(), parsed.customerId()); + assertEquals(order.amountCents(), parsed.amountCents()); + assertEquals(order.createdAt(), parsed.createdAt()); + } + + // ---- Scalar types ---- + + @Test + public void testEchoScalarTypes() { + ScalarTypes scalars = + new ScalarTypes( + 3.14, + 2.71f, + 42, + 9876543210L, + 100, + 200L, + -50, + -100L, + 999, + 888L, + -777, + -666L, + true, + "hello world", + ByteString.copyFromUtf8("binary data")); + ScalarTypes parsed = echoClient.echoScalarTypes(scalars); + assertEquals(scalars.doubleVal(), parsed.doubleVal(), 0.0001); + assertEquals(scalars.floatVal(), parsed.floatVal(), 0.0001f); + assertEquals(scalars.int32Val(), parsed.int32Val()); + assertEquals(scalars.int64Val(), parsed.int64Val()); + assertEquals(scalars.uint32Val(), parsed.uint32Val()); + assertEquals(scalars.uint64Val(), parsed.uint64Val()); + assertEquals(scalars.sint32Val(), parsed.sint32Val()); + assertEquals(scalars.sint64Val(), parsed.sint64Val()); + assertEquals(scalars.fixed32Val(), parsed.fixed32Val()); + assertEquals(scalars.fixed64Val(), parsed.fixed64Val()); + assertEquals(scalars.sfixed32Val(), parsed.sfixed32Val()); + assertEquals(scalars.sfixed64Val(), parsed.sfixed64Val()); + assertEquals(scalars.boolVal(), parsed.boolVal()); + assertEquals(scalars.stringVal(), parsed.stringVal()); + assertEquals(scalars.bytesVal(), parsed.bytesVal()); + } + + // ---- Enum tests ---- + + @Test + public void testEnumToValueFromValueRoundTrip() { + for (OrderStatus status : OrderStatus.values()) { + Integer wireValue = status.toValue(); + OrderStatus back = OrderStatus.fromValue(wireValue); + assertEquals(status, back); + } + } + + @Test + public void testEnumForce() { + assertEquals(OrderStatus.ORDER_STATUS_PENDING, OrderStatus.force("ORDER_STATUS_PENDING")); + } + + @Test(expected = RuntimeException.class) + public void testEnumForceInvalid() { + OrderStatus.force("NONEXISTENT"); + } + + @Test(expected = IllegalArgumentException.class) + public void testEnumFromValueInvalid() { + OrderStatus.fromValue(999); + } + + @Test + public void testPriorityEnumRoundTrip() { + for (Priority p : Priority.values()) { + Integer wireValue = p.toValue(); + Priority back = Priority.fromValue(wireValue); + assertEquals(p, back); + } + } + + // ---- Optional fields ---- + + @Test + public void testEchoOptionalFieldsAllPresent() { + OptionalFields opt = + new OptionalFields(Optional.of("Alice"), Optional.of(30), Optional.of(testCustomer)); + OptionalFields parsed = echoClient.echoOptionalFields(opt); + assertEquals(Optional.of("Alice"), parsed.name()); + assertEquals(Optional.of(30), parsed.age()); + assertTrue(parsed.customer().isPresent()); + assertEquals("CUST-123", parsed.customer().get().customerId().unwrap()); + } + + @Test + public void testEchoOptionalFieldsAllEmpty() { + OptionalFields opt = new OptionalFields(Optional.empty(), Optional.empty(), Optional.empty()); + OptionalFields parsed = echoClient.echoOptionalFields(opt); + assertEquals(Optional.empty(), parsed.name()); + assertEquals(Optional.empty(), parsed.age()); + assertEquals(Optional.empty(), parsed.customer()); + } + + @Test + public void testEchoOptionalFieldsPartiallyPresent() { + OptionalFields opt = new OptionalFields(Optional.of("Bob"), Optional.empty(), Optional.empty()); + OptionalFields parsed = echoClient.echoOptionalFields(opt); + assertEquals(Optional.of("Bob"), parsed.name()); + assertEquals(Optional.empty(), parsed.age()); + assertEquals(Optional.empty(), parsed.customer()); + } + + // ---- Nested messages ---- + + @Test + public void testEchoOuter() { + Outer outer = new Outer("outer-name", new Inner(42, "inner-desc")); + Outer parsed = echoClient.echoOuter(outer); + assertEquals("outer-name", parsed.name()); + assertNotNull(parsed.inner()); + assertEquals(Integer.valueOf(42), parsed.inner().value()); + assertEquals("inner-desc", parsed.inner().description()); + } + + // ---- OneOf types ---- + + @Test + public void testEchoPaymentMethodCreditCard() { + CreditCard cc = new CreditCard("4111111111111111", "12/25", "123"); + PaymentMethodMethod method = new PaymentMethodMethod.CreditCardValue(cc); + PaymentMethod pm = new PaymentMethod("PAY-1", method); + PaymentMethod parsed = echoClient.echoPaymentMethod(pm); + assertEquals("PAY-1", parsed.id()); + assertTrue(parsed.method() instanceof PaymentMethodMethod.CreditCardValue); + PaymentMethodMethod.CreditCardValue ccv = (PaymentMethodMethod.CreditCardValue) parsed.method(); + assertEquals("4111111111111111", ccv.creditCard().cardNumber()); + assertEquals("12/25", ccv.creditCard().expiryDate()); + assertEquals("123", ccv.creditCard().cvv()); + } + + @Test + public void testEchoPaymentMethodBankTransfer() { + BankTransfer bt = new BankTransfer("123456789", "021000021"); + PaymentMethodMethod method = new PaymentMethodMethod.BankTransferValue(bt); + PaymentMethod pm = new PaymentMethod("PAY-2", method); + PaymentMethod parsed = echoClient.echoPaymentMethod(pm); + assertEquals("PAY-2", parsed.id()); + assertTrue(parsed.method() instanceof PaymentMethodMethod.BankTransferValue); + PaymentMethodMethod.BankTransferValue btv = + (PaymentMethodMethod.BankTransferValue) parsed.method(); + assertEquals("123456789", btv.bankTransfer().accountNumber()); + assertEquals("021000021", btv.bankTransfer().routingNumber()); + } + + @Test + public void testEchoPaymentMethodWallet() { + Wallet w = new Wallet("wallet-42", "Stripe"); + PaymentMethodMethod method = new PaymentMethodMethod.WalletValue(w); + PaymentMethod pm = new PaymentMethod("PAY-3", method); + PaymentMethod parsed = echoClient.echoPaymentMethod(pm); + assertEquals("PAY-3", parsed.id()); + assertTrue(parsed.method() instanceof PaymentMethodMethod.WalletValue); + PaymentMethodMethod.WalletValue wv = (PaymentMethodMethod.WalletValue) parsed.method(); + assertEquals("wallet-42", wv.wallet().walletId()); + assertEquals("Stripe", wv.wallet().provider()); + } + + @Test + public void testEchoNotificationWithEmailTarget() { + Notification notif = + new Notification( + "Hello!", Priority.PRIORITY_HIGH, new NotificationTarget.Email("user@example.com")); + Notification parsed = echoClient.echoNotification(notif); + assertEquals("Hello!", parsed.message()); + assertEquals(Priority.PRIORITY_HIGH, parsed.priority()); + assertTrue(parsed.target() instanceof NotificationTarget.Email); + assertEquals("user@example.com", ((NotificationTarget.Email) parsed.target()).email()); + } + + @Test + public void testEchoNotificationWithPhoneTarget() { + Notification notif = + new Notification( + "Alert", Priority.PRIORITY_CRITICAL, new NotificationTarget.Phone("+1234567890")); + Notification parsed = echoClient.echoNotification(notif); + assertEquals("Alert", parsed.message()); + assertEquals(Priority.PRIORITY_CRITICAL, parsed.priority()); + assertTrue(parsed.target() instanceof NotificationTarget.Phone); + assertEquals("+1234567890", ((NotificationTarget.Phone) parsed.target()).phone()); + } + + @Test + public void testEchoNotificationWithWebhookTarget() { + Notification notif = + new Notification( + "Event", + Priority.PRIORITY_LOW, + new NotificationTarget.WebhookUrl("https://hooks.example.com/abc")); + Notification parsed = echoClient.echoNotification(notif); + assertEquals("Event", parsed.message()); + assertEquals(Priority.PRIORITY_LOW, parsed.priority()); + assertTrue(parsed.target() instanceof NotificationTarget.WebhookUrl); + assertEquals( + "https://hooks.example.com/abc", + ((NotificationTarget.WebhookUrl) parsed.target()).webhookUrl()); + } + + // ---- Collections ---- + + @Test + public void testEchoInventory() { + List productIds = List.of("PROD-1", "PROD-2", "PROD-3"); + Map stockCounts = new LinkedHashMap<>(); + stockCounts.put("PROD-1", 100); + stockCounts.put("PROD-2", 200); + stockCounts.put("PROD-3", 0); + List orders = + List.of( + new Order( + OrderId.valueOf("ORD-1"), + CustomerId.valueOf("CUST-1"), + 1000L, + Instant.ofEpochSecond(1000, 0)), + new Order( + OrderId.valueOf("ORD-2"), + CustomerId.valueOf("CUST-2"), + 2000L, + Instant.ofEpochSecond(2000, 0))); + + Inventory inventory = new Inventory("WH-1", productIds, stockCounts, orders); + Inventory parsed = echoClient.echoInventory(inventory); + assertEquals("WH-1", parsed.warehouseId()); + assertEquals(3, parsed.productIds().size()); + assertEquals("PROD-1", parsed.productIds().get(0)); + assertEquals("PROD-2", parsed.productIds().get(1)); + assertEquals("PROD-3", parsed.productIds().get(2)); + assertEquals(3, parsed.stockCounts().size()); + assertEquals(Integer.valueOf(100), parsed.stockCounts().get("PROD-1")); + assertEquals(Integer.valueOf(200), parsed.stockCounts().get("PROD-2")); + assertEquals(Integer.valueOf(0), parsed.stockCounts().get("PROD-3")); + assertEquals(2, parsed.recentOrders().size()); + assertEquals("ORD-1", parsed.recentOrders().get(0).orderId().unwrap()); + assertEquals("ORD-2", parsed.recentOrders().get(1).orderId().unwrap()); + } + + @Test + public void testEchoInventoryEmptyCollections() { + Inventory inventory = new Inventory("WH-EMPTY", List.of(), Map.of(), List.of()); + Inventory parsed = echoClient.echoInventory(inventory); + assertEquals("WH-EMPTY", parsed.warehouseId()); + assertTrue(parsed.productIds().isEmpty()); + assertTrue(parsed.stockCounts().isEmpty()); + assertTrue(parsed.recentOrders().isEmpty()); + } + + // ---- Well-known types ---- + + @Test + public void testEchoWellKnownTypes() { + WellKnownTypesMessage msg = + new WellKnownTypesMessage( + Instant.ofEpochSecond(1700000000L, 123456789), + Duration.ofSeconds(3600, 500000000), + Optional.of("hello"), + Optional.of(42), + Optional.of(true)); + WellKnownTypesMessage parsed = echoClient.echoWellKnownTypes(msg); + assertEquals(msg.createdAt(), parsed.createdAt()); + assertEquals(msg.ttl(), parsed.ttl()); + assertEquals(Optional.of("hello"), parsed.nullableString()); + assertEquals(Optional.of(42), parsed.nullableInt()); + assertEquals(Optional.of(true), parsed.nullableBool()); + } + + // ---- Wrapper ID types ---- + + @Test + public void testCustomerIdValueOf() { + CustomerId id = CustomerId.valueOf("abc"); + assertEquals("abc", id.unwrap()); + assertEquals("abc", id.toString()); + } + + @Test + public void testOrderIdValueOf() { + OrderId id = OrderId.valueOf("ORD-1"); + assertEquals("ORD-1", id.unwrap()); + assertEquals("ORD-1", id.toString()); + } + + // ---- With methods ---- + + @Test + public void testCustomerWithMethods() { + Customer updated = testCustomer.withName("Jane Doe"); + assertEquals("Jane Doe", updated.name()); + assertEquals(testCustomer.customerId(), updated.customerId()); + assertEquals(testCustomer.email(), updated.email()); + } + + @Test + public void testOrderWithMethods() { + Order order = + new Order( + OrderId.valueOf("ORD-1"), + CustomerId.valueOf("CUST-1"), + 1000L, + Instant.ofEpochSecond(1000, 0)); + Order updated = order.withAmountCents(2000L); + assertEquals(Long.valueOf(2000L), updated.amountCents()); + assertEquals(order.orderId(), updated.orderId()); + } + + // ---- Echo with empty strings ---- + + @Test + public void testEchoCustomerEmptyStrings() { + Customer empty = new Customer(CustomerId.valueOf(""), "", ""); + Customer parsed = echoClient.echoCustomer(empty); + assertEquals("", parsed.customerId().unwrap()); + assertEquals("", parsed.name()); + assertEquals("", parsed.email()); + } +} diff --git a/testers/grpc/java/generated-and-checked-in/com/example/grpc/BankTransfer.java b/testers/grpc/java/generated-and-checked-in/com/example/grpc/BankTransfer.java new file mode 100644 index 0000000000..1fceb9c0ba --- /dev/null +++ b/testers/grpc/java/generated-and-checked-in/com/example/grpc/BankTransfer.java @@ -0,0 +1,74 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record BankTransfer(String accountNumber, String routingNumber) { + public BankTransfer withAccountNumber(String accountNumber) { + return new BankTransfer(accountNumber, routingNumber); + } + + public BankTransfer withRoutingNumber(String routingNumber) { + return new BankTransfer(accountNumber, routingNumber); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(BankTransfer value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public BankTransfer parse(InputStream stream) { + try { + return BankTransfer.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static BankTransfer parseFrom(CodedInputStream input) throws IOException { + String accountNumber = ""; + String routingNumber = ""; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + accountNumber = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + routingNumber = input.readString(); + } else { + input.skipField(tag); + } + ; + } + ; + return new BankTransfer(accountNumber, routingNumber); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeStringSize(1, this.accountNumber()); + size = size + CodedOutputStream.computeStringSize(2, this.routingNumber()); + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeString(1, this.accountNumber()); + output.writeString(2, this.routingNumber()); + } +} diff --git a/testers/grpc/java/generated-and-checked-in/com/example/grpc/ChatMessage.java b/testers/grpc/java/generated-and-checked-in/com/example/grpc/ChatMessage.java new file mode 100644 index 0000000000..d144c468e5 --- /dev/null +++ b/testers/grpc/java/generated-and-checked-in/com/example/grpc/ChatMessage.java @@ -0,0 +1,114 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.time.Instant; + +public record ChatMessage(String sender, String content, Instant sentAt) { + public ChatMessage withSender(String sender) { + return new ChatMessage(sender, content, sentAt); + } + + public ChatMessage withContent(String content) { + return new ChatMessage(sender, content, sentAt); + } + + public ChatMessage withSentAt(Instant sentAt) { + return new ChatMessage(sender, content, sentAt); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(ChatMessage value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public ChatMessage parse(InputStream stream) { + try { + return ChatMessage.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static ChatMessage parseFrom(CodedInputStream input) throws IOException { + String sender = ""; + String content = ""; + Instant sentAt = Instant.EPOCH; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + sender = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + content = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 3) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + var _tsSeconds = 0L; + var _tsNanos = 0; + while (!input.isAtEnd()) { + var _tsTag = input.readTag(); + if (WireFormat.getTagFieldNumber(_tsTag) == 1) { + _tsSeconds = input.readInt64(); + } else if (WireFormat.getTagFieldNumber(_tsTag) == 2) { + _tsNanos = input.readInt32(); + } else { + input.skipField(_tsTag); + } + ; + } + ; + sentAt = Instant.ofEpochSecond(_tsSeconds, (long) (_tsNanos)); + input.popLimit(_oldLimit); + ; + } else { + input.skipField(tag); + } + ; + } + ; + return new ChatMessage(sender, content, sentAt); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeStringSize(1, this.sender()); + size = size + CodedOutputStream.computeStringSize(2, this.content()); + size = + size + + CodedOutputStream.computeTagSize(3) + + CodedOutputStream.computeUInt32SizeNoTag( + CodedOutputStream.computeInt64Size(1, this.sentAt().getEpochSecond()) + + CodedOutputStream.computeInt32Size(2, this.sentAt().getNano())) + + CodedOutputStream.computeInt64Size(1, this.sentAt().getEpochSecond()) + + CodedOutputStream.computeInt32Size(2, this.sentAt().getNano()); + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeString(1, this.sender()); + output.writeString(2, this.content()); + output.writeTag(3, 2); + output.writeUInt32NoTag( + CodedOutputStream.computeInt64Size(1, this.sentAt().getEpochSecond()) + + CodedOutputStream.computeInt32Size(2, this.sentAt().getNano())); + output.writeInt64(1, this.sentAt().getEpochSecond()); + output.writeInt32(2, this.sentAt().getNano()); + } +} diff --git a/testers/grpc/java/generated-and-checked-in/com/example/grpc/CreateOrderRequest.java b/testers/grpc/java/generated-and-checked-in/com/example/grpc/CreateOrderRequest.java new file mode 100644 index 0000000000..2682d832d3 --- /dev/null +++ b/testers/grpc/java/generated-and-checked-in/com/example/grpc/CreateOrderRequest.java @@ -0,0 +1,80 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record CreateOrderRequest(Order order) { + public CreateOrderRequest withOrder(Order order) { + return new CreateOrderRequest(order); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(CreateOrderRequest value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public CreateOrderRequest parse(InputStream stream) { + try { + return CreateOrderRequest.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static CreateOrderRequest parseFrom(CodedInputStream input) throws IOException { + Order order = null; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + order = Order.parseFrom(input); + input.popLimit(_oldLimit); + ; + } else { + input.skipField(tag); + } + ; + } + ; + return new CreateOrderRequest(order); + } + + public Integer getSerializedSize() { + Integer size = 0; + if (!this.order().equals(null)) { + size = + size + + CodedOutputStream.computeTagSize(1) + + CodedOutputStream.computeUInt32SizeNoTag(this.order().getSerializedSize()) + + this.order().getSerializedSize(); + } + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + if (!this.order().equals(null)) { + output.writeTag(1, 2); + output.writeUInt32NoTag(this.order().getSerializedSize()); + this.order().writeTo(output); + ; + } + } +} diff --git a/testers/grpc/java/generated-and-checked-in/com/example/grpc/CreateOrderResponse.java b/testers/grpc/java/generated-and-checked-in/com/example/grpc/CreateOrderResponse.java new file mode 100644 index 0000000000..405cc5b05f --- /dev/null +++ b/testers/grpc/java/generated-and-checked-in/com/example/grpc/CreateOrderResponse.java @@ -0,0 +1,74 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record CreateOrderResponse(String orderId, OrderStatus status) { + public CreateOrderResponse withOrderId(String orderId) { + return new CreateOrderResponse(orderId, status); + } + + public CreateOrderResponse withStatus(OrderStatus status) { + return new CreateOrderResponse(orderId, status); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(CreateOrderResponse value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public CreateOrderResponse parse(InputStream stream) { + try { + return CreateOrderResponse.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static CreateOrderResponse parseFrom(CodedInputStream input) throws IOException { + String orderId = ""; + OrderStatus status = OrderStatus.fromValue(0); + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + orderId = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + status = OrderStatus.fromValue(input.readEnum()); + } else { + input.skipField(tag); + } + ; + } + ; + return new CreateOrderResponse(orderId, status); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeStringSize(1, this.orderId()); + size = size + CodedOutputStream.computeEnumSize(2, this.status().toValue()); + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeString(1, this.orderId()); + output.writeEnum(2, this.status().toValue()); + } +} diff --git a/testers/grpc/java/generated-and-checked-in/com/example/grpc/CreditCard.java b/testers/grpc/java/generated-and-checked-in/com/example/grpc/CreditCard.java new file mode 100644 index 0000000000..9ab67e6743 --- /dev/null +++ b/testers/grpc/java/generated-and-checked-in/com/example/grpc/CreditCard.java @@ -0,0 +1,83 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record CreditCard(String cardNumber, String expiryDate, String cvv) { + public CreditCard withCardNumber(String cardNumber) { + return new CreditCard(cardNumber, expiryDate, cvv); + } + + public CreditCard withExpiryDate(String expiryDate) { + return new CreditCard(cardNumber, expiryDate, cvv); + } + + public CreditCard withCvv(String cvv) { + return new CreditCard(cardNumber, expiryDate, cvv); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(CreditCard value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public CreditCard parse(InputStream stream) { + try { + return CreditCard.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static CreditCard parseFrom(CodedInputStream input) throws IOException { + String cardNumber = ""; + String expiryDate = ""; + String cvv = ""; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + cardNumber = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + expiryDate = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 3) { + cvv = input.readString(); + } else { + input.skipField(tag); + } + ; + } + ; + return new CreditCard(cardNumber, expiryDate, cvv); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeStringSize(1, this.cardNumber()); + size = size + CodedOutputStream.computeStringSize(2, this.expiryDate()); + size = size + CodedOutputStream.computeStringSize(3, this.cvv()); + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeString(1, this.cardNumber()); + output.writeString(2, this.expiryDate()); + output.writeString(3, this.cvv()); + } +} diff --git a/testers/grpc/java/generated-and-checked-in/com/example/grpc/Customer.java b/testers/grpc/java/generated-and-checked-in/com/example/grpc/Customer.java new file mode 100644 index 0000000000..9b8f1e46aa --- /dev/null +++ b/testers/grpc/java/generated-and-checked-in/com/example/grpc/Customer.java @@ -0,0 +1,83 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record Customer(CustomerId customerId, String name, String email) { + public Customer withCustomerId(CustomerId customerId) { + return new Customer(customerId, name, email); + } + + public Customer withName(String name) { + return new Customer(customerId, name, email); + } + + public Customer withEmail(String email) { + return new Customer(customerId, name, email); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(Customer value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public Customer parse(InputStream stream) { + try { + return Customer.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static Customer parseFrom(CodedInputStream input) throws IOException { + CustomerId customerId = CustomerId.valueOf(""); + String name = ""; + String email = ""; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + customerId = CustomerId.valueOf(input.readString()); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + name = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 3) { + email = input.readString(); + } else { + input.skipField(tag); + } + ; + } + ; + return new Customer(customerId, name, email); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeStringSize(1, this.customerId().unwrap()); + size = size + CodedOutputStream.computeStringSize(2, this.name()); + size = size + CodedOutputStream.computeStringSize(3, this.email()); + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeString(1, this.customerId().unwrap()); + output.writeString(2, this.name()); + output.writeString(3, this.email()); + } +} diff --git a/testers/grpc/java/generated-and-checked-in/com/example/grpc/CustomerId.java b/testers/grpc/java/generated-and-checked-in/com/example/grpc/CustomerId.java new file mode 100644 index 0000000000..5cc7de21b7 --- /dev/null +++ b/testers/grpc/java/generated-and-checked-in/com/example/grpc/CustomerId.java @@ -0,0 +1,23 @@ +package com.example.grpc; + +/** Wrapper type for typr.grpc.GrpcCodegen$$$Lambda/0x00007fc001118400@69e1dd28 */ +public record CustomerId(String value) { + public CustomerId withValue(String value) { + return new CustomerId(value); + } + + @Override + public java.lang.String toString() { + return value.toString(); + } + + /** Create a CustomerId from a raw value */ + public static CustomerId valueOf(String v) { + return new CustomerId(v); + } + + /** Get the underlying value */ + public String unwrap() { + return this.value(); + } +} diff --git a/testers/grpc/java/generated-and-checked-in/com/example/grpc/EchoService.java b/testers/grpc/java/generated-and-checked-in/com/example/grpc/EchoService.java new file mode 100644 index 0000000000..c88b0ece5c --- /dev/null +++ b/testers/grpc/java/generated-and-checked-in/com/example/grpc/EchoService.java @@ -0,0 +1,22 @@ +package com.example.grpc; + +/** Clean service interface for EchoService gRPC service */ +public interface EchoService { + ScalarTypes echoScalarTypes(ScalarTypes request); + + Customer echoCustomer(Customer request); + + Order echoOrder(Order request); + + Inventory echoInventory(Inventory request); + + Outer echoOuter(Outer request); + + OptionalFields echoOptionalFields(OptionalFields request); + + WellKnownTypesMessage echoWellKnownTypes(WellKnownTypesMessage request); + + PaymentMethod echoPaymentMethod(PaymentMethod request); + + Notification echoNotification(Notification request); +} diff --git a/testers/grpc/java/generated-and-checked-in/com/example/grpc/EchoServiceClient.java b/testers/grpc/java/generated-and-checked-in/com/example/grpc/EchoServiceClient.java new file mode 100644 index 0000000000..0c66971277 --- /dev/null +++ b/testers/grpc/java/generated-and-checked-in/com/example/grpc/EchoServiceClient.java @@ -0,0 +1,126 @@ +package com.example.grpc; + +import io.grpc.CallOptions; +import io.grpc.Channel; +import io.grpc.MethodDescriptor; +import io.grpc.MethodDescriptor.MethodType; +import io.grpc.stub.ClientCalls; + +/** gRPC client wrapper for EchoService - wraps Channel with clean types */ +public class EchoServiceClient implements EchoService { + Channel channel; + + public EchoServiceClient(Channel channel) { + this.channel = channel; + } + + public static MethodDescriptor ECHO_CUSTOMER = + MethodDescriptor.newBuilder(Customer.MARSHALLER, Customer.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoCustomer") + .build(); + + public static MethodDescriptor ECHO_INVENTORY = + MethodDescriptor.newBuilder(Inventory.MARSHALLER, Inventory.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoInventory") + .build(); + + public static MethodDescriptor ECHO_NOTIFICATION = + MethodDescriptor.newBuilder(Notification.MARSHALLER, Notification.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoNotification") + .build(); + + public static MethodDescriptor ECHO_OPTIONAL_FIELDS = + MethodDescriptor.newBuilder(OptionalFields.MARSHALLER, OptionalFields.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoOptionalFields") + .build(); + + public static MethodDescriptor ECHO_ORDER = + MethodDescriptor.newBuilder(Order.MARSHALLER, Order.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoOrder") + .build(); + + public static MethodDescriptor ECHO_OUTER = + MethodDescriptor.newBuilder(Outer.MARSHALLER, Outer.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoOuter") + .build(); + + public static MethodDescriptor ECHO_PAYMENT_METHOD = + MethodDescriptor.newBuilder(PaymentMethod.MARSHALLER, PaymentMethod.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoPaymentMethod") + .build(); + + public static MethodDescriptor ECHO_SCALAR_TYPES = + MethodDescriptor.newBuilder(ScalarTypes.MARSHALLER, ScalarTypes.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoScalarTypes") + .build(); + + public static MethodDescriptor + ECHO_WELL_KNOWN_TYPES = + MethodDescriptor.newBuilder( + WellKnownTypesMessage.MARSHALLER, WellKnownTypesMessage.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoWellKnownTypes") + .build(); + + @Override + public ScalarTypes echoScalarTypes(ScalarTypes request) { + return ClientCalls.blockingUnaryCall( + channel, EchoServiceClient.ECHO_SCALAR_TYPES, CallOptions.DEFAULT, request); + } + + @Override + public Customer echoCustomer(Customer request) { + return ClientCalls.blockingUnaryCall( + channel, EchoServiceClient.ECHO_CUSTOMER, CallOptions.DEFAULT, request); + } + + @Override + public Order echoOrder(Order request) { + return ClientCalls.blockingUnaryCall( + channel, EchoServiceClient.ECHO_ORDER, CallOptions.DEFAULT, request); + } + + @Override + public Inventory echoInventory(Inventory request) { + return ClientCalls.blockingUnaryCall( + channel, EchoServiceClient.ECHO_INVENTORY, CallOptions.DEFAULT, request); + } + + @Override + public Outer echoOuter(Outer request) { + return ClientCalls.blockingUnaryCall( + channel, EchoServiceClient.ECHO_OUTER, CallOptions.DEFAULT, request); + } + + @Override + public OptionalFields echoOptionalFields(OptionalFields request) { + return ClientCalls.blockingUnaryCall( + channel, EchoServiceClient.ECHO_OPTIONAL_FIELDS, CallOptions.DEFAULT, request); + } + + @Override + public WellKnownTypesMessage echoWellKnownTypes(WellKnownTypesMessage request) { + return ClientCalls.blockingUnaryCall( + channel, EchoServiceClient.ECHO_WELL_KNOWN_TYPES, CallOptions.DEFAULT, request); + } + + @Override + public PaymentMethod echoPaymentMethod(PaymentMethod request) { + return ClientCalls.blockingUnaryCall( + channel, EchoServiceClient.ECHO_PAYMENT_METHOD, CallOptions.DEFAULT, request); + } + + @Override + public Notification echoNotification(Notification request) { + return ClientCalls.blockingUnaryCall( + channel, EchoServiceClient.ECHO_NOTIFICATION, CallOptions.DEFAULT, request); + } +} diff --git a/testers/grpc/java/generated-and-checked-in/com/example/grpc/EchoServiceServer.java b/testers/grpc/java/generated-and-checked-in/com/example/grpc/EchoServiceServer.java new file mode 100644 index 0000000000..79adb0a7ac --- /dev/null +++ b/testers/grpc/java/generated-and-checked-in/com/example/grpc/EchoServiceServer.java @@ -0,0 +1,141 @@ +package com.example.grpc; + +import io.grpc.BindableService; +import io.grpc.MethodDescriptor; +import io.grpc.MethodDescriptor.MethodType; +import io.grpc.ServerServiceDefinition; +import io.grpc.stub.ServerCalls; + +/** gRPC server adapter for EchoService - delegates to clean service interface */ +public class EchoServiceServer implements BindableService { + EchoService delegate; + + public EchoServiceServer(EchoService delegate) { + this.delegate = delegate; + } + + public static MethodDescriptor ECHO_CUSTOMER = + MethodDescriptor.newBuilder(Customer.MARSHALLER, Customer.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoCustomer") + .build(); + + public static MethodDescriptor ECHO_INVENTORY = + MethodDescriptor.newBuilder(Inventory.MARSHALLER, Inventory.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoInventory") + .build(); + + public static MethodDescriptor ECHO_NOTIFICATION = + MethodDescriptor.newBuilder(Notification.MARSHALLER, Notification.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoNotification") + .build(); + + public static MethodDescriptor ECHO_OPTIONAL_FIELDS = + MethodDescriptor.newBuilder(OptionalFields.MARSHALLER, OptionalFields.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoOptionalFields") + .build(); + + public static MethodDescriptor ECHO_ORDER = + MethodDescriptor.newBuilder(Order.MARSHALLER, Order.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoOrder") + .build(); + + public static MethodDescriptor ECHO_OUTER = + MethodDescriptor.newBuilder(Outer.MARSHALLER, Outer.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoOuter") + .build(); + + public static MethodDescriptor ECHO_PAYMENT_METHOD = + MethodDescriptor.newBuilder(PaymentMethod.MARSHALLER, PaymentMethod.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoPaymentMethod") + .build(); + + public static MethodDescriptor ECHO_SCALAR_TYPES = + MethodDescriptor.newBuilder(ScalarTypes.MARSHALLER, ScalarTypes.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoScalarTypes") + .build(); + + public static MethodDescriptor + ECHO_WELL_KNOWN_TYPES = + MethodDescriptor.newBuilder( + WellKnownTypesMessage.MARSHALLER, WellKnownTypesMessage.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.EchoService/EchoWellKnownTypes") + .build(); + + @Override + public ServerServiceDefinition bindService() { + return ServerServiceDefinition.builder("testgrpc.EchoService") + .addMethod( + EchoServiceServer.ECHO_SCALAR_TYPES, + ServerCalls.asyncUnaryCall( + (request, responseObserver) -> { + responseObserver.onNext(delegate.echoScalarTypes(request)); + responseObserver.onCompleted(); + })) + .addMethod( + EchoServiceServer.ECHO_CUSTOMER, + ServerCalls.asyncUnaryCall( + (request, responseObserver) -> { + responseObserver.onNext(delegate.echoCustomer(request)); + responseObserver.onCompleted(); + })) + .addMethod( + EchoServiceServer.ECHO_ORDER, + ServerCalls.asyncUnaryCall( + (request, responseObserver) -> { + responseObserver.onNext(delegate.echoOrder(request)); + responseObserver.onCompleted(); + })) + .addMethod( + EchoServiceServer.ECHO_INVENTORY, + ServerCalls.asyncUnaryCall( + (request, responseObserver) -> { + responseObserver.onNext(delegate.echoInventory(request)); + responseObserver.onCompleted(); + })) + .addMethod( + EchoServiceServer.ECHO_OUTER, + ServerCalls.asyncUnaryCall( + (request, responseObserver) -> { + responseObserver.onNext(delegate.echoOuter(request)); + responseObserver.onCompleted(); + })) + .addMethod( + EchoServiceServer.ECHO_OPTIONAL_FIELDS, + ServerCalls.asyncUnaryCall( + (request, responseObserver) -> { + responseObserver.onNext(delegate.echoOptionalFields(request)); + responseObserver.onCompleted(); + })) + .addMethod( + EchoServiceServer.ECHO_WELL_KNOWN_TYPES, + ServerCalls.asyncUnaryCall( + (request, responseObserver) -> { + responseObserver.onNext(delegate.echoWellKnownTypes(request)); + responseObserver.onCompleted(); + })) + .addMethod( + EchoServiceServer.ECHO_PAYMENT_METHOD, + ServerCalls.asyncUnaryCall( + (request, responseObserver) -> { + responseObserver.onNext(delegate.echoPaymentMethod(request)); + responseObserver.onCompleted(); + })) + .addMethod( + EchoServiceServer.ECHO_NOTIFICATION, + ServerCalls.asyncUnaryCall( + (request, responseObserver) -> { + responseObserver.onNext(delegate.echoNotification(request)); + responseObserver.onCompleted(); + })) + .build(); + } +} diff --git a/testers/grpc/java/generated-and-checked-in/com/example/grpc/GetCustomerRequest.java b/testers/grpc/java/generated-and-checked-in/com/example/grpc/GetCustomerRequest.java new file mode 100644 index 0000000000..5d30c0ed46 --- /dev/null +++ b/testers/grpc/java/generated-and-checked-in/com/example/grpc/GetCustomerRequest.java @@ -0,0 +1,65 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record GetCustomerRequest(String customerId) { + public GetCustomerRequest withCustomerId(String customerId) { + return new GetCustomerRequest(customerId); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(GetCustomerRequest value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public GetCustomerRequest parse(InputStream stream) { + try { + return GetCustomerRequest.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static GetCustomerRequest parseFrom(CodedInputStream input) throws IOException { + String customerId = ""; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + customerId = input.readString(); + } else { + input.skipField(tag); + } + ; + } + ; + return new GetCustomerRequest(customerId); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeStringSize(1, this.customerId()); + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeString(1, this.customerId()); + } +} diff --git a/testers/grpc/java/generated-and-checked-in/com/example/grpc/GetCustomerResponse.java b/testers/grpc/java/generated-and-checked-in/com/example/grpc/GetCustomerResponse.java new file mode 100644 index 0000000000..3a21596211 --- /dev/null +++ b/testers/grpc/java/generated-and-checked-in/com/example/grpc/GetCustomerResponse.java @@ -0,0 +1,80 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record GetCustomerResponse(Customer customer) { + public GetCustomerResponse withCustomer(Customer customer) { + return new GetCustomerResponse(customer); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(GetCustomerResponse value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public GetCustomerResponse parse(InputStream stream) { + try { + return GetCustomerResponse.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static GetCustomerResponse parseFrom(CodedInputStream input) throws IOException { + Customer customer = null; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + customer = Customer.parseFrom(input); + input.popLimit(_oldLimit); + ; + } else { + input.skipField(tag); + } + ; + } + ; + return new GetCustomerResponse(customer); + } + + public Integer getSerializedSize() { + Integer size = 0; + if (!this.customer().equals(null)) { + size = + size + + CodedOutputStream.computeTagSize(1) + + CodedOutputStream.computeUInt32SizeNoTag(this.customer().getSerializedSize()) + + this.customer().getSerializedSize(); + } + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + if (!this.customer().equals(null)) { + output.writeTag(1, 2); + output.writeUInt32NoTag(this.customer().getSerializedSize()); + this.customer().writeTo(output); + ; + } + } +} diff --git a/testers/grpc/java/generated-and-checked-in/com/example/grpc/Inner.java b/testers/grpc/java/generated-and-checked-in/com/example/grpc/Inner.java new file mode 100644 index 0000000000..4b45f35f6d --- /dev/null +++ b/testers/grpc/java/generated-and-checked-in/com/example/grpc/Inner.java @@ -0,0 +1,74 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record Inner(Integer value, String description) { + public Inner withValue(Integer value) { + return new Inner(value, description); + } + + public Inner withDescription(String description) { + return new Inner(value, description); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(Inner value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public Inner parse(InputStream stream) { + try { + return Inner.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static Inner parseFrom(CodedInputStream input) throws IOException { + Integer value = 0; + String description = ""; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + value = input.readInt32(); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + description = input.readString(); + } else { + input.skipField(tag); + } + ; + } + ; + return new Inner(value, description); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeInt32Size(1, this.value()); + size = size + CodedOutputStream.computeStringSize(2, this.description()); + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeInt32(1, this.value()); + output.writeString(2, this.description()); + } +} diff --git a/testers/grpc/java/generated-and-checked-in/com/example/grpc/Inventory.java b/testers/grpc/java/generated-and-checked-in/com/example/grpc/Inventory.java new file mode 100644 index 0000000000..0e8b63bcc4 --- /dev/null +++ b/testers/grpc/java/generated-and-checked-in/com/example/grpc/Inventory.java @@ -0,0 +1,159 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +public record Inventory( + String warehouseId, + List productIds, + Map stockCounts, + List recentOrders) { + public Inventory withWarehouseId(String warehouseId) { + return new Inventory(warehouseId, productIds, stockCounts, recentOrders); + } + + public Inventory withProductIds(List productIds) { + return new Inventory(warehouseId, productIds, stockCounts, recentOrders); + } + + public Inventory withStockCounts(Map stockCounts) { + return new Inventory(warehouseId, productIds, stockCounts, recentOrders); + } + + public Inventory withRecentOrders(List recentOrders) { + return new Inventory(warehouseId, productIds, stockCounts, recentOrders); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(Inventory value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public Inventory parse(InputStream stream) { + try { + return Inventory.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static Inventory parseFrom(CodedInputStream input) throws IOException { + String warehouseId = ""; + ArrayList productIds = new ArrayList<>(); + HashMap stockCounts = new HashMap(); + ArrayList recentOrders = new ArrayList<>(); + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + warehouseId = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + productIds.add(input.readString()); + } else if (WireFormat.getTagFieldNumber(tag) == 3) { + var length = input.readRawVarint32(); + var oldLimit = input.pushLimit(length); + var mapKey = ""; + var mapValue = 0; + while (!input.isAtEnd()) { + var entryTag = input.readTag(); + if (WireFormat.getTagFieldNumber(entryTag) == 1) { + mapKey = input.readString(); + } else if (WireFormat.getTagFieldNumber(entryTag) == 2) { + mapValue = input.readInt32(); + } else { + input.skipField(entryTag); + } + ; + } + ; + input.popLimit(oldLimit); + stockCounts.put(mapKey, mapValue); + ; + } else if (WireFormat.getTagFieldNumber(tag) == 4) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + recentOrders.add(Order.parseFrom(input)); + input.popLimit(_oldLimit); + ; + } else { + input.skipField(tag); + } + ; + } + ; + return new Inventory(warehouseId, productIds, stockCounts, recentOrders); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeStringSize(1, this.warehouseId()); + for (String elem : this.productIds()) { + size = size + CodedOutputStream.computeStringSize(2, elem); + } + ; + for (Entry entry : this.stockCounts().entrySet()) { + size = + size + + CodedOutputStream.computeTagSize(3) + + CodedOutputStream.computeUInt32SizeNoTag( + CodedOutputStream.computeStringSize(1, entry.getKey()) + + CodedOutputStream.computeInt32Size(2, entry.getValue())) + + CodedOutputStream.computeStringSize(1, entry.getKey()) + + CodedOutputStream.computeInt32Size(2, entry.getValue()); + } + ; + for (Order elem : this.recentOrders()) { + size = + size + + CodedOutputStream.computeTagSize(4) + + CodedOutputStream.computeUInt32SizeNoTag(elem.getSerializedSize()) + + elem.getSerializedSize(); + } + ; + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeString(1, this.warehouseId()); + for (String elem : this.productIds()) { + output.writeString(2, elem); + } + ; + for (Entry entry : this.stockCounts().entrySet()) { + output.writeTag(3, 2); + output.writeUInt32NoTag( + CodedOutputStream.computeStringSize(1, entry.getKey()) + + CodedOutputStream.computeInt32Size(2, entry.getValue())); + output.writeString(1, entry.getKey()); + output.writeInt32(2, entry.getValue()); + } + ; + for (Order elem : this.recentOrders()) { + output.writeTag(4, 2); + output.writeUInt32NoTag(elem.getSerializedSize()); + elem.writeTo(output); + } + ; + } +} diff --git a/testers/grpc/java/generated-and-checked-in/com/example/grpc/ListOrdersRequest.java b/testers/grpc/java/generated-and-checked-in/com/example/grpc/ListOrdersRequest.java new file mode 100644 index 0000000000..3a12489bec --- /dev/null +++ b/testers/grpc/java/generated-and-checked-in/com/example/grpc/ListOrdersRequest.java @@ -0,0 +1,74 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record ListOrdersRequest(String customerId, Integer pageSize) { + public ListOrdersRequest withCustomerId(String customerId) { + return new ListOrdersRequest(customerId, pageSize); + } + + public ListOrdersRequest withPageSize(Integer pageSize) { + return new ListOrdersRequest(customerId, pageSize); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(ListOrdersRequest value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public ListOrdersRequest parse(InputStream stream) { + try { + return ListOrdersRequest.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static ListOrdersRequest parseFrom(CodedInputStream input) throws IOException { + String customerId = ""; + Integer pageSize = 0; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + customerId = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + pageSize = input.readInt32(); + } else { + input.skipField(tag); + } + ; + } + ; + return new ListOrdersRequest(customerId, pageSize); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeStringSize(1, this.customerId()); + size = size + CodedOutputStream.computeInt32Size(2, this.pageSize()); + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeString(1, this.customerId()); + output.writeInt32(2, this.pageSize()); + } +} diff --git a/testers/grpc/java/generated-and-checked-in/com/example/grpc/Notification.java b/testers/grpc/java/generated-and-checked-in/com/example/grpc/Notification.java new file mode 100644 index 0000000000..908f7b7844 --- /dev/null +++ b/testers/grpc/java/generated-and-checked-in/com/example/grpc/Notification.java @@ -0,0 +1,102 @@ +package com.example.grpc; + +import com.example.grpc.NotificationTarget.Email; +import com.example.grpc.NotificationTarget.Phone; +import com.example.grpc.NotificationTarget.WebhookUrl; +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record Notification(String message, Priority priority, NotificationTarget target) { + public Notification withMessage(String message) { + return new Notification(message, priority, target); + } + + public Notification withPriority(Priority priority) { + return new Notification(message, priority, target); + } + + public Notification withTarget(NotificationTarget target) { + return new Notification(message, priority, target); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(Notification value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public Notification parse(InputStream stream) { + try { + return Notification.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static Notification parseFrom(CodedInputStream input) throws IOException { + String message = ""; + Priority priority = Priority.fromValue(0); + NotificationTarget target = null; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + message = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + priority = Priority.fromValue(input.readEnum()); + } else if (WireFormat.getTagFieldNumber(tag) == 3) { + target = new Email(input.readString()); + } else if (WireFormat.getTagFieldNumber(tag) == 4) { + target = new Phone(input.readString()); + } else if (WireFormat.getTagFieldNumber(tag) == 5) { + target = new WebhookUrl(input.readString()); + } else { + input.skipField(tag); + } + ; + } + ; + return new Notification(message, priority, target); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeStringSize(1, this.message()); + size = size + CodedOutputStream.computeEnumSize(2, this.priority().toValue()); + switch (this.target()) { + case null -> {} + case Email c -> size = size + CodedOutputStream.computeStringSize(3, c.email()); + case Phone c -> size = size + CodedOutputStream.computeStringSize(4, c.phone()); + case WebhookUrl c -> size = size + CodedOutputStream.computeStringSize(5, c.webhookUrl()); + } + ; + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeString(1, this.message()); + output.writeEnum(2, this.priority().toValue()); + switch (this.target()) { + case null -> {} + case Email c -> output.writeString(3, c.email()); + case Phone c -> output.writeString(4, c.phone()); + case WebhookUrl c -> output.writeString(5, c.webhookUrl()); + } + ; + } +} diff --git a/testers/grpc/java/generated-and-checked-in/com/example/grpc/NotificationTarget.java b/testers/grpc/java/generated-and-checked-in/com/example/grpc/NotificationTarget.java new file mode 100644 index 0000000000..f95bbe8b48 --- /dev/null +++ b/testers/grpc/java/generated-and-checked-in/com/example/grpc/NotificationTarget.java @@ -0,0 +1,23 @@ +package com.example.grpc; + +/** OneOf type for target */ +public sealed interface NotificationTarget + permits NotificationTarget.Email, NotificationTarget.Phone, NotificationTarget.WebhookUrl { + record Email(String email) implements NotificationTarget { + public Email withEmail(String email) { + return new Email(email); + } + } + + record Phone(String phone) implements NotificationTarget { + public Phone withPhone(String phone) { + return new Phone(phone); + } + } + + record WebhookUrl(String webhookUrl) implements NotificationTarget { + public WebhookUrl withWebhookUrl(String webhookUrl) { + return new WebhookUrl(webhookUrl); + } + } +} diff --git a/testers/grpc/java/generated-and-checked-in/com/example/grpc/OptionalFields.java b/testers/grpc/java/generated-and-checked-in/com/example/grpc/OptionalFields.java new file mode 100644 index 0000000000..534c31a211 --- /dev/null +++ b/testers/grpc/java/generated-and-checked-in/com/example/grpc/OptionalFields.java @@ -0,0 +1,119 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Optional; + +public record OptionalFields( + Optional name, Optional age, Optional customer) { + public OptionalFields withName(Optional name) { + return new OptionalFields(name, age, customer); + } + + public OptionalFields withAge(Optional age) { + return new OptionalFields(name, age, customer); + } + + public OptionalFields withCustomer(Optional customer) { + return new OptionalFields(name, age, customer); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(OptionalFields value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public OptionalFields parse(InputStream stream) { + try { + return OptionalFields.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static OptionalFields parseFrom(CodedInputStream input) throws IOException { + Optional name = Optional.empty(); + Optional age = Optional.empty(); + Optional customer = Optional.empty(); + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + name = Optional.of(input.readString()); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + age = Optional.of(input.readInt32()); + } else if (WireFormat.getTagFieldNumber(tag) == 3) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + customer = Optional.of(Customer.parseFrom(input)); + input.popLimit(_oldLimit); + ; + } else { + input.skipField(tag); + } + ; + } + ; + return new OptionalFields(name, age, customer); + } + + public Integer getSerializedSize() { + Integer size = 0; + if (this.name().isPresent()) { + var v = this.name().get(); + size = size + CodedOutputStream.computeStringSize(1, v); + ; + } + if (this.age().isPresent()) { + var v = this.age().get(); + size = size + CodedOutputStream.computeInt32Size(2, v); + ; + } + if (this.customer().isPresent()) { + var v = this.customer().get(); + size = + size + + CodedOutputStream.computeTagSize(3) + + CodedOutputStream.computeUInt32SizeNoTag(v.getSerializedSize()) + + v.getSerializedSize(); + ; + } + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + if (this.name().isPresent()) { + var v = this.name().get(); + output.writeString(1, v); + ; + } + if (this.age().isPresent()) { + var v = this.age().get(); + output.writeInt32(2, v); + ; + } + if (this.customer().isPresent()) { + var v = this.customer().get(); + output.writeTag(3, 2); + output.writeUInt32NoTag(v.getSerializedSize()); + v.writeTo(output); + ; + } + } +} diff --git a/testers/grpc/java/generated-and-checked-in/com/example/grpc/Order.java b/testers/grpc/java/generated-and-checked-in/com/example/grpc/Order.java new file mode 100644 index 0000000000..a1dd05fa0b --- /dev/null +++ b/testers/grpc/java/generated-and-checked-in/com/example/grpc/Order.java @@ -0,0 +1,123 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.time.Instant; + +public record Order(OrderId orderId, CustomerId customerId, Long amountCents, Instant createdAt) { + public Order withOrderId(OrderId orderId) { + return new Order(orderId, customerId, amountCents, createdAt); + } + + public Order withCustomerId(CustomerId customerId) { + return new Order(orderId, customerId, amountCents, createdAt); + } + + public Order withAmountCents(Long amountCents) { + return new Order(orderId, customerId, amountCents, createdAt); + } + + public Order withCreatedAt(Instant createdAt) { + return new Order(orderId, customerId, amountCents, createdAt); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(Order value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public Order parse(InputStream stream) { + try { + return Order.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static Order parseFrom(CodedInputStream input) throws IOException { + OrderId orderId = OrderId.valueOf(""); + CustomerId customerId = CustomerId.valueOf(""); + Long amountCents = 0L; + Instant createdAt = Instant.EPOCH; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + orderId = OrderId.valueOf(input.readString()); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + customerId = CustomerId.valueOf(input.readString()); + } else if (WireFormat.getTagFieldNumber(tag) == 3) { + amountCents = input.readInt64(); + } else if (WireFormat.getTagFieldNumber(tag) == 4) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + var _tsSeconds = 0L; + var _tsNanos = 0; + while (!input.isAtEnd()) { + var _tsTag = input.readTag(); + if (WireFormat.getTagFieldNumber(_tsTag) == 1) { + _tsSeconds = input.readInt64(); + } else if (WireFormat.getTagFieldNumber(_tsTag) == 2) { + _tsNanos = input.readInt32(); + } else { + input.skipField(_tsTag); + } + ; + } + ; + createdAt = Instant.ofEpochSecond(_tsSeconds, (long) (_tsNanos)); + input.popLimit(_oldLimit); + ; + } else { + input.skipField(tag); + } + ; + } + ; + return new Order(orderId, customerId, amountCents, createdAt); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeStringSize(1, this.orderId().unwrap()); + size = size + CodedOutputStream.computeStringSize(2, this.customerId().unwrap()); + size = size + CodedOutputStream.computeInt64Size(3, this.amountCents()); + size = + size + + CodedOutputStream.computeTagSize(4) + + CodedOutputStream.computeUInt32SizeNoTag( + CodedOutputStream.computeInt64Size(1, this.createdAt().getEpochSecond()) + + CodedOutputStream.computeInt32Size(2, this.createdAt().getNano())) + + CodedOutputStream.computeInt64Size(1, this.createdAt().getEpochSecond()) + + CodedOutputStream.computeInt32Size(2, this.createdAt().getNano()); + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeString(1, this.orderId().unwrap()); + output.writeString(2, this.customerId().unwrap()); + output.writeInt64(3, this.amountCents()); + output.writeTag(4, 2); + output.writeUInt32NoTag( + CodedOutputStream.computeInt64Size(1, this.createdAt().getEpochSecond()) + + CodedOutputStream.computeInt32Size(2, this.createdAt().getNano())); + output.writeInt64(1, this.createdAt().getEpochSecond()); + output.writeInt32(2, this.createdAt().getNano()); + } +} diff --git a/testers/grpc/java/generated-and-checked-in/com/example/grpc/OrderId.java b/testers/grpc/java/generated-and-checked-in/com/example/grpc/OrderId.java new file mode 100644 index 0000000000..6db357af07 --- /dev/null +++ b/testers/grpc/java/generated-and-checked-in/com/example/grpc/OrderId.java @@ -0,0 +1,23 @@ +package com.example.grpc; + +/** Wrapper type for typr.grpc.GrpcCodegen$$$Lambda/0x00007fc001118400@53976f5c */ +public record OrderId(String value) { + public OrderId withValue(String value) { + return new OrderId(value); + } + + @Override + public java.lang.String toString() { + return value.toString(); + } + + /** Create a OrderId from a raw value */ + public static OrderId valueOf(String v) { + return new OrderId(v); + } + + /** Get the underlying value */ + public String unwrap() { + return this.value(); + } +} diff --git a/testers/grpc/java/generated-and-checked-in/com/example/grpc/OrderService.java b/testers/grpc/java/generated-and-checked-in/com/example/grpc/OrderService.java new file mode 100644 index 0000000000..743491970c --- /dev/null +++ b/testers/grpc/java/generated-and-checked-in/com/example/grpc/OrderService.java @@ -0,0 +1,16 @@ +package com.example.grpc; + +import java.util.Iterator; + +/** Clean service interface for OrderService gRPC service */ +public interface OrderService { + GetCustomerResponse getCustomer(GetCustomerRequest request); + + CreateOrderResponse createOrder(CreateOrderRequest request); + + Iterator listOrders(ListOrdersRequest request); + + OrderSummary submitOrders(Iterator requests); + + Iterator chat(Iterator requests); +} diff --git a/testers/grpc/java/generated-and-checked-in/com/example/grpc/OrderServiceClient.java b/testers/grpc/java/generated-and-checked-in/com/example/grpc/OrderServiceClient.java new file mode 100644 index 0000000000..4ee6085081 --- /dev/null +++ b/testers/grpc/java/generated-and-checked-in/com/example/grpc/OrderServiceClient.java @@ -0,0 +1,76 @@ +package com.example.grpc; + +import io.grpc.CallOptions; +import io.grpc.Channel; +import io.grpc.MethodDescriptor; +import io.grpc.MethodDescriptor.MethodType; +import io.grpc.stub.ClientCalls; +import java.util.Iterator; + +/** gRPC client wrapper for OrderService - wraps Channel with clean types */ +public class OrderServiceClient implements OrderService { + Channel channel; + + public OrderServiceClient(Channel channel) { + this.channel = channel; + } + + public static MethodDescriptor CHAT = + MethodDescriptor.newBuilder(ChatMessage.MARSHALLER, ChatMessage.MARSHALLER) + .setType(MethodType.BIDI_STREAMING) + .setFullMethodName("testgrpc.OrderService/Chat") + .build(); + + public static MethodDescriptor CREATE_ORDER = + MethodDescriptor.newBuilder(CreateOrderRequest.MARSHALLER, CreateOrderResponse.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.OrderService/CreateOrder") + .build(); + + public static MethodDescriptor GET_CUSTOMER = + MethodDescriptor.newBuilder(GetCustomerRequest.MARSHALLER, GetCustomerResponse.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.OrderService/GetCustomer") + .build(); + + public static MethodDescriptor LIST_ORDERS = + MethodDescriptor.newBuilder(ListOrdersRequest.MARSHALLER, OrderUpdate.MARSHALLER) + .setType(MethodType.SERVER_STREAMING) + .setFullMethodName("testgrpc.OrderService/ListOrders") + .build(); + + public static MethodDescriptor SUBMIT_ORDERS = + MethodDescriptor.newBuilder(CreateOrderRequest.MARSHALLER, OrderSummary.MARSHALLER) + .setType(MethodType.CLIENT_STREAMING) + .setFullMethodName("testgrpc.OrderService/SubmitOrders") + .build(); + + @Override + public GetCustomerResponse getCustomer(GetCustomerRequest request) { + return ClientCalls.blockingUnaryCall( + channel, OrderServiceClient.GET_CUSTOMER, CallOptions.DEFAULT, request); + } + + @Override + public CreateOrderResponse createOrder(CreateOrderRequest request) { + return ClientCalls.blockingUnaryCall( + channel, OrderServiceClient.CREATE_ORDER, CallOptions.DEFAULT, request); + } + + @Override + public Iterator listOrders(ListOrdersRequest request) { + return ClientCalls.blockingServerStreamingCall( + channel, OrderServiceClient.LIST_ORDERS, CallOptions.DEFAULT, request); + } + + @Override + public OrderSummary submitOrders(Iterator requests) { + throw new UnsupportedOperationException( + "Client streaming not yet implemented in client wrapper"); + } + + @Override + public Iterator chat(Iterator requests) { + throw new UnsupportedOperationException("Bidi streaming not yet implemented in client wrapper"); + } +} diff --git a/testers/grpc/java/generated-and-checked-in/com/example/grpc/OrderServiceServer.java b/testers/grpc/java/generated-and-checked-in/com/example/grpc/OrderServiceServer.java new file mode 100644 index 0000000000..12f5572448 --- /dev/null +++ b/testers/grpc/java/generated-and-checked-in/com/example/grpc/OrderServiceServer.java @@ -0,0 +1,91 @@ +package com.example.grpc; + +import io.grpc.BindableService; +import io.grpc.MethodDescriptor; +import io.grpc.MethodDescriptor.MethodType; +import io.grpc.ServerServiceDefinition; +import io.grpc.stub.ServerCalls; + +/** gRPC server adapter for OrderService - delegates to clean service interface */ +public class OrderServiceServer implements BindableService { + OrderService delegate; + + public OrderServiceServer(OrderService delegate) { + this.delegate = delegate; + } + + public static MethodDescriptor CHAT = + MethodDescriptor.newBuilder(ChatMessage.MARSHALLER, ChatMessage.MARSHALLER) + .setType(MethodType.BIDI_STREAMING) + .setFullMethodName("testgrpc.OrderService/Chat") + .build(); + + public static MethodDescriptor CREATE_ORDER = + MethodDescriptor.newBuilder(CreateOrderRequest.MARSHALLER, CreateOrderResponse.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.OrderService/CreateOrder") + .build(); + + public static MethodDescriptor GET_CUSTOMER = + MethodDescriptor.newBuilder(GetCustomerRequest.MARSHALLER, GetCustomerResponse.MARSHALLER) + .setType(MethodType.UNARY) + .setFullMethodName("testgrpc.OrderService/GetCustomer") + .build(); + + public static MethodDescriptor LIST_ORDERS = + MethodDescriptor.newBuilder(ListOrdersRequest.MARSHALLER, OrderUpdate.MARSHALLER) + .setType(MethodType.SERVER_STREAMING) + .setFullMethodName("testgrpc.OrderService/ListOrders") + .build(); + + public static MethodDescriptor SUBMIT_ORDERS = + MethodDescriptor.newBuilder(CreateOrderRequest.MARSHALLER, OrderSummary.MARSHALLER) + .setType(MethodType.CLIENT_STREAMING) + .setFullMethodName("testgrpc.OrderService/SubmitOrders") + .build(); + + @Override + public ServerServiceDefinition bindService() { + return ServerServiceDefinition.builder("testgrpc.OrderService") + .addMethod( + OrderServiceServer.GET_CUSTOMER, + ServerCalls.asyncUnaryCall( + (request, responseObserver) -> { + responseObserver.onNext(delegate.getCustomer(request)); + responseObserver.onCompleted(); + })) + .addMethod( + OrderServiceServer.CREATE_ORDER, + ServerCalls.asyncUnaryCall( + (request, responseObserver) -> { + responseObserver.onNext(delegate.createOrder(request)); + responseObserver.onCompleted(); + })) + .addMethod( + OrderServiceServer.LIST_ORDERS, + ServerCalls.asyncServerStreamingCall( + (request, responseObserver) -> { + var results = delegate.listOrders(request); + while (results.hasNext()) { + responseObserver.onNext(results.next()); + } + ; + responseObserver.onCompleted(); + })) + .addMethod( + OrderServiceServer.SUBMIT_ORDERS, + ServerCalls.asyncClientStreamingCall( + responseObserver -> { + throw new UnsupportedOperationException( + "Client streaming not yet implemented in server adapter"); + })) + .addMethod( + OrderServiceServer.CHAT, + ServerCalls.asyncBidiStreamingCall( + responseObserver -> { + throw new UnsupportedOperationException( + "Bidi streaming not yet implemented in server adapter"); + })) + .build(); + } +} diff --git a/testers/grpc/java/generated-and-checked-in/com/example/grpc/OrderStatus.java b/testers/grpc/java/generated-and-checked-in/com/example/grpc/OrderStatus.java new file mode 100644 index 0000000000..c0496984dd --- /dev/null +++ b/testers/grpc/java/generated-and-checked-in/com/example/grpc/OrderStatus.java @@ -0,0 +1,73 @@ +package com.example.grpc; + +public enum OrderStatus { + ORDER_STATUS_UNSPECIFIED("ORDER_STATUS_UNSPECIFIED"), + ORDER_STATUS_PENDING("ORDER_STATUS_PENDING"), + ORDER_STATUS_PROCESSING("ORDER_STATUS_PROCESSING"), + ORDER_STATUS_SHIPPED("ORDER_STATUS_SHIPPED"), + ORDER_STATUS_DELIVERED("ORDER_STATUS_DELIVERED"), + ORDER_STATUS_CANCELLED("ORDER_STATUS_CANCELLED"); + final java.lang.String value; + + public java.lang.String value() { + return value; + } + + OrderStatus(java.lang.String value) { + this.value = value; + } + + public static final java.lang.String Names = + java.util.Arrays.stream(OrderStatus.values()) + .map(x -> x.value) + .collect(java.util.stream.Collectors.joining(", ")); + public static final java.util.Map ByName = + java.util.Arrays.stream(OrderStatus.values()) + .collect(java.util.stream.Collectors.toMap(n -> n.value, n -> n)); + + public Integer toValue() { + if (this.toString().equals("ORDER_STATUS_UNSPECIFIED")) { + return 0; + } else if (this.toString().equals("ORDER_STATUS_PENDING")) { + return 1; + } else if (this.toString().equals("ORDER_STATUS_PROCESSING")) { + return 2; + } else if (this.toString().equals("ORDER_STATUS_SHIPPED")) { + return 3; + } else if (this.toString().equals("ORDER_STATUS_DELIVERED")) { + return 4; + } else if (this.toString().equals("ORDER_STATUS_CANCELLED")) { + return 5; + } else { + return 0; + } + } + + public static OrderStatus fromValue(Integer value) { + if (value == 0) { + return OrderStatus.ORDER_STATUS_UNSPECIFIED; + } else if (value == 1) { + return OrderStatus.ORDER_STATUS_PENDING; + } else if (value == 2) { + return OrderStatus.ORDER_STATUS_PROCESSING; + } else if (value == 3) { + return OrderStatus.ORDER_STATUS_SHIPPED; + } else if (value == 4) { + return OrderStatus.ORDER_STATUS_DELIVERED; + } else if (value == 5) { + return OrderStatus.ORDER_STATUS_CANCELLED; + } else { + throw new IllegalArgumentException("Unknown enum value: " + value); + } + } + ; + + public static OrderStatus force(java.lang.String str) { + if (ByName.containsKey(str)) { + return ByName.get(str); + } else { + throw new RuntimeException( + "'" + str + "' does not match any of the following legal values: " + Names); + } + } +} diff --git a/testers/grpc/java/generated-and-checked-in/com/example/grpc/OrderSummary.java b/testers/grpc/java/generated-and-checked-in/com/example/grpc/OrderSummary.java new file mode 100644 index 0000000000..e2750a4e1d --- /dev/null +++ b/testers/grpc/java/generated-and-checked-in/com/example/grpc/OrderSummary.java @@ -0,0 +1,74 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record OrderSummary(Integer totalOrders, Long totalAmountCents) { + public OrderSummary withTotalOrders(Integer totalOrders) { + return new OrderSummary(totalOrders, totalAmountCents); + } + + public OrderSummary withTotalAmountCents(Long totalAmountCents) { + return new OrderSummary(totalOrders, totalAmountCents); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(OrderSummary value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public OrderSummary parse(InputStream stream) { + try { + return OrderSummary.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static OrderSummary parseFrom(CodedInputStream input) throws IOException { + Integer totalOrders = 0; + Long totalAmountCents = 0L; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + totalOrders = input.readInt32(); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + totalAmountCents = input.readInt64(); + } else { + input.skipField(tag); + } + ; + } + ; + return new OrderSummary(totalOrders, totalAmountCents); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeInt32Size(1, this.totalOrders()); + size = size + CodedOutputStream.computeInt64Size(2, this.totalAmountCents()); + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeInt32(1, this.totalOrders()); + output.writeInt64(2, this.totalAmountCents()); + } +} diff --git a/testers/grpc/java/generated-and-checked-in/com/example/grpc/OrderUpdate.java b/testers/grpc/java/generated-and-checked-in/com/example/grpc/OrderUpdate.java new file mode 100644 index 0000000000..ed13d01723 --- /dev/null +++ b/testers/grpc/java/generated-and-checked-in/com/example/grpc/OrderUpdate.java @@ -0,0 +1,114 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.time.Instant; + +public record OrderUpdate(String orderId, OrderStatus status, Instant updatedAt) { + public OrderUpdate withOrderId(String orderId) { + return new OrderUpdate(orderId, status, updatedAt); + } + + public OrderUpdate withStatus(OrderStatus status) { + return new OrderUpdate(orderId, status, updatedAt); + } + + public OrderUpdate withUpdatedAt(Instant updatedAt) { + return new OrderUpdate(orderId, status, updatedAt); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(OrderUpdate value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public OrderUpdate parse(InputStream stream) { + try { + return OrderUpdate.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static OrderUpdate parseFrom(CodedInputStream input) throws IOException { + String orderId = ""; + OrderStatus status = OrderStatus.fromValue(0); + Instant updatedAt = Instant.EPOCH; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + orderId = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + status = OrderStatus.fromValue(input.readEnum()); + } else if (WireFormat.getTagFieldNumber(tag) == 3) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + var _tsSeconds = 0L; + var _tsNanos = 0; + while (!input.isAtEnd()) { + var _tsTag = input.readTag(); + if (WireFormat.getTagFieldNumber(_tsTag) == 1) { + _tsSeconds = input.readInt64(); + } else if (WireFormat.getTagFieldNumber(_tsTag) == 2) { + _tsNanos = input.readInt32(); + } else { + input.skipField(_tsTag); + } + ; + } + ; + updatedAt = Instant.ofEpochSecond(_tsSeconds, (long) (_tsNanos)); + input.popLimit(_oldLimit); + ; + } else { + input.skipField(tag); + } + ; + } + ; + return new OrderUpdate(orderId, status, updatedAt); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeStringSize(1, this.orderId()); + size = size + CodedOutputStream.computeEnumSize(2, this.status().toValue()); + size = + size + + CodedOutputStream.computeTagSize(3) + + CodedOutputStream.computeUInt32SizeNoTag( + CodedOutputStream.computeInt64Size(1, this.updatedAt().getEpochSecond()) + + CodedOutputStream.computeInt32Size(2, this.updatedAt().getNano())) + + CodedOutputStream.computeInt64Size(1, this.updatedAt().getEpochSecond()) + + CodedOutputStream.computeInt32Size(2, this.updatedAt().getNano()); + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeString(1, this.orderId()); + output.writeEnum(2, this.status().toValue()); + output.writeTag(3, 2); + output.writeUInt32NoTag( + CodedOutputStream.computeInt64Size(1, this.updatedAt().getEpochSecond()) + + CodedOutputStream.computeInt32Size(2, this.updatedAt().getNano())); + output.writeInt64(1, this.updatedAt().getEpochSecond()); + output.writeInt32(2, this.updatedAt().getNano()); + } +} diff --git a/testers/grpc/java/generated-and-checked-in/com/example/grpc/Outer.java b/testers/grpc/java/generated-and-checked-in/com/example/grpc/Outer.java new file mode 100644 index 0000000000..e60998c822 --- /dev/null +++ b/testers/grpc/java/generated-and-checked-in/com/example/grpc/Outer.java @@ -0,0 +1,89 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record Outer(String name, Inner inner) { + public Outer withName(String name) { + return new Outer(name, inner); + } + + public Outer withInner(Inner inner) { + return new Outer(name, inner); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(Outer value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public Outer parse(InputStream stream) { + try { + return Outer.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static Outer parseFrom(CodedInputStream input) throws IOException { + String name = ""; + Inner inner = null; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + name = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + inner = Inner.parseFrom(input); + input.popLimit(_oldLimit); + ; + } else { + input.skipField(tag); + } + ; + } + ; + return new Outer(name, inner); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeStringSize(1, this.name()); + if (!this.inner().equals(null)) { + size = + size + + CodedOutputStream.computeTagSize(2) + + CodedOutputStream.computeUInt32SizeNoTag(this.inner().getSerializedSize()) + + this.inner().getSerializedSize(); + } + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeString(1, this.name()); + if (!this.inner().equals(null)) { + output.writeTag(2, 2); + output.writeUInt32NoTag(this.inner().getSerializedSize()); + this.inner().writeTo(output); + ; + } + } +} diff --git a/testers/grpc/java/generated-and-checked-in/com/example/grpc/PaymentMethod.java b/testers/grpc/java/generated-and-checked-in/com/example/grpc/PaymentMethod.java new file mode 100644 index 0000000000..329bb47f66 --- /dev/null +++ b/testers/grpc/java/generated-and-checked-in/com/example/grpc/PaymentMethod.java @@ -0,0 +1,132 @@ +package com.example.grpc; + +import com.example.grpc.PaymentMethodMethod.BankTransferValue; +import com.example.grpc.PaymentMethodMethod.CreditCardValue; +import com.example.grpc.PaymentMethodMethod.WalletValue; +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record PaymentMethod(String id, PaymentMethodMethod method) { + public PaymentMethod withId(String id) { + return new PaymentMethod(id, method); + } + + public PaymentMethod withMethod(PaymentMethodMethod method) { + return new PaymentMethod(id, method); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(PaymentMethod value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public PaymentMethod parse(InputStream stream) { + try { + return PaymentMethod.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static PaymentMethod parseFrom(CodedInputStream input) throws IOException { + String id = ""; + PaymentMethodMethod method = null; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + id = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + method = new CreditCardValue(CreditCard.parseFrom(input)); + input.popLimit(_oldLimit); + ; + } else if (WireFormat.getTagFieldNumber(tag) == 3) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + method = new BankTransferValue(BankTransfer.parseFrom(input)); + input.popLimit(_oldLimit); + ; + } else if (WireFormat.getTagFieldNumber(tag) == 4) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + method = new WalletValue(Wallet.parseFrom(input)); + input.popLimit(_oldLimit); + ; + } else { + input.skipField(tag); + } + ; + } + ; + return new PaymentMethod(id, method); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeStringSize(1, this.id()); + switch (this.method()) { + case null -> {} + case CreditCardValue c -> + size = + size + + CodedOutputStream.computeTagSize(2) + + CodedOutputStream.computeUInt32SizeNoTag(c.creditCard().getSerializedSize()) + + c.creditCard().getSerializedSize(); + case BankTransferValue c -> + size = + size + + CodedOutputStream.computeTagSize(3) + + CodedOutputStream.computeUInt32SizeNoTag(c.bankTransfer().getSerializedSize()) + + c.bankTransfer().getSerializedSize(); + case WalletValue c -> + size = + size + + CodedOutputStream.computeTagSize(4) + + CodedOutputStream.computeUInt32SizeNoTag(c.wallet().getSerializedSize()) + + c.wallet().getSerializedSize(); + } + ; + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeString(1, this.id()); + switch (this.method()) { + case null -> {} + case CreditCardValue c -> { + output.writeTag(2, 2); + output.writeUInt32NoTag(c.creditCard().getSerializedSize()); + c.creditCard().writeTo(output); + } + case BankTransferValue c -> { + output.writeTag(3, 2); + output.writeUInt32NoTag(c.bankTransfer().getSerializedSize()); + c.bankTransfer().writeTo(output); + } + case WalletValue c -> { + output.writeTag(4, 2); + output.writeUInt32NoTag(c.wallet().getSerializedSize()); + c.wallet().writeTo(output); + } + } + ; + } +} diff --git a/testers/grpc/java/generated-and-checked-in/com/example/grpc/PaymentMethodMethod.java b/testers/grpc/java/generated-and-checked-in/com/example/grpc/PaymentMethodMethod.java new file mode 100644 index 0000000000..7772f87dae --- /dev/null +++ b/testers/grpc/java/generated-and-checked-in/com/example/grpc/PaymentMethodMethod.java @@ -0,0 +1,25 @@ +package com.example.grpc; + +/** OneOf type for method */ +public sealed interface PaymentMethodMethod + permits PaymentMethodMethod.CreditCardValue, + PaymentMethodMethod.BankTransferValue, + PaymentMethodMethod.WalletValue { + record BankTransferValue(BankTransfer bankTransfer) implements PaymentMethodMethod { + public BankTransferValue withBankTransfer(BankTransfer bankTransfer) { + return new BankTransferValue(bankTransfer); + } + } + + record CreditCardValue(CreditCard creditCard) implements PaymentMethodMethod { + public CreditCardValue withCreditCard(CreditCard creditCard) { + return new CreditCardValue(creditCard); + } + } + + record WalletValue(Wallet wallet) implements PaymentMethodMethod { + public WalletValue withWallet(Wallet wallet) { + return new WalletValue(wallet); + } + } +} diff --git a/testers/grpc/java/generated-and-checked-in/com/example/grpc/Priority.java b/testers/grpc/java/generated-and-checked-in/com/example/grpc/Priority.java new file mode 100644 index 0000000000..d11a842342 --- /dev/null +++ b/testers/grpc/java/generated-and-checked-in/com/example/grpc/Priority.java @@ -0,0 +1,68 @@ +package com.example.grpc; + +public enum Priority { + PRIORITY_UNSPECIFIED("PRIORITY_UNSPECIFIED"), + PRIORITY_LOW("PRIORITY_LOW"), + PRIORITY_MEDIUM("PRIORITY_MEDIUM"), + PRIORITY_HIGH("PRIORITY_HIGH"), + PRIORITY_CRITICAL("PRIORITY_CRITICAL"); + final java.lang.String value; + + public java.lang.String value() { + return value; + } + + Priority(java.lang.String value) { + this.value = value; + } + + public static final java.lang.String Names = + java.util.Arrays.stream(Priority.values()) + .map(x -> x.value) + .collect(java.util.stream.Collectors.joining(", ")); + public static final java.util.Map ByName = + java.util.Arrays.stream(Priority.values()) + .collect(java.util.stream.Collectors.toMap(n -> n.value, n -> n)); + + public Integer toValue() { + if (this.toString().equals("PRIORITY_UNSPECIFIED")) { + return 0; + } else if (this.toString().equals("PRIORITY_LOW")) { + return 1; + } else if (this.toString().equals("PRIORITY_MEDIUM")) { + return 2; + } else if (this.toString().equals("PRIORITY_HIGH")) { + return 3; + } else if (this.toString().equals("PRIORITY_CRITICAL")) { + return 4; + } else { + return 0; + } + } + + public static Priority fromValue(Integer value) { + if (value == 0) { + return Priority.PRIORITY_UNSPECIFIED; + } else if (value == 1) { + return Priority.PRIORITY_LOW; + } else if (value == 2) { + return Priority.PRIORITY_MEDIUM; + } else if (value == 3) { + return Priority.PRIORITY_HIGH; + } else if (value == 4) { + return Priority.PRIORITY_CRITICAL; + } else { + throw new IllegalArgumentException("Unknown enum value: " + value); + } + } + ; + + public static Priority force(java.lang.String str) { + if (ByName.containsKey(str)) { + return ByName.get(str); + } else { + throw new RuntimeException( + "'" + str + "' does not match any of the following legal values: " + Names); + } + } +} diff --git a/testers/grpc/java/generated-and-checked-in/com/example/grpc/ScalarTypes.java b/testers/grpc/java/generated-and-checked-in/com/example/grpc/ScalarTypes.java new file mode 100644 index 0000000000..3117c27eda --- /dev/null +++ b/testers/grpc/java/generated-and-checked-in/com/example/grpc/ScalarTypes.java @@ -0,0 +1,447 @@ +package com.example.grpc; + +import com.google.protobuf.ByteString; +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record ScalarTypes( + Double doubleVal, + Float floatVal, + Integer int32Val, + Long int64Val, + Integer uint32Val, + Long uint64Val, + Integer sint32Val, + Long sint64Val, + Integer fixed32Val, + Long fixed64Val, + Integer sfixed32Val, + Long sfixed64Val, + Boolean boolVal, + String stringVal, + ByteString bytesVal) { + public ScalarTypes withDoubleVal(Double doubleVal) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public ScalarTypes withFloatVal(Float floatVal) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public ScalarTypes withInt32Val(Integer int32Val) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public ScalarTypes withInt64Val(Long int64Val) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public ScalarTypes withUint32Val(Integer uint32Val) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public ScalarTypes withUint64Val(Long uint64Val) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public ScalarTypes withSint32Val(Integer sint32Val) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public ScalarTypes withSint64Val(Long sint64Val) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public ScalarTypes withFixed32Val(Integer fixed32Val) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public ScalarTypes withFixed64Val(Long fixed64Val) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public ScalarTypes withSfixed32Val(Integer sfixed32Val) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public ScalarTypes withSfixed64Val(Long sfixed64Val) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public ScalarTypes withBoolVal(Boolean boolVal) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public ScalarTypes withStringVal(String stringVal) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public ScalarTypes withBytesVal(ByteString bytesVal) { + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(ScalarTypes value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public ScalarTypes parse(InputStream stream) { + try { + return ScalarTypes.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static ScalarTypes parseFrom(CodedInputStream input) throws IOException { + Double doubleVal = 0.0; + Float floatVal = 0.0f; + Integer int32Val = 0; + Long int64Val = 0L; + Integer uint32Val = 0; + Long uint64Val = 0L; + Integer sint32Val = 0; + Long sint64Val = 0L; + Integer fixed32Val = 0; + Long fixed64Val = 0L; + Integer sfixed32Val = 0; + Long sfixed64Val = 0L; + Boolean boolVal = false; + String stringVal = ""; + ByteString bytesVal = ByteString.EMPTY; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + doubleVal = input.readDouble(); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + floatVal = input.readFloat(); + } else if (WireFormat.getTagFieldNumber(tag) == 3) { + int32Val = input.readInt32(); + } else if (WireFormat.getTagFieldNumber(tag) == 4) { + int64Val = input.readInt64(); + } else if (WireFormat.getTagFieldNumber(tag) == 5) { + uint32Val = input.readUInt32(); + } else if (WireFormat.getTagFieldNumber(tag) == 6) { + uint64Val = input.readUInt64(); + } else if (WireFormat.getTagFieldNumber(tag) == 7) { + sint32Val = input.readSInt32(); + } else if (WireFormat.getTagFieldNumber(tag) == 8) { + sint64Val = input.readSInt64(); + } else if (WireFormat.getTagFieldNumber(tag) == 9) { + fixed32Val = input.readFixed32(); + } else if (WireFormat.getTagFieldNumber(tag) == 10) { + fixed64Val = input.readFixed64(); + } else if (WireFormat.getTagFieldNumber(tag) == 11) { + sfixed32Val = input.readSFixed32(); + } else if (WireFormat.getTagFieldNumber(tag) == 12) { + sfixed64Val = input.readSFixed64(); + } else if (WireFormat.getTagFieldNumber(tag) == 13) { + boolVal = input.readBool(); + } else if (WireFormat.getTagFieldNumber(tag) == 14) { + stringVal = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 15) { + bytesVal = input.readBytes(); + } else { + input.skipField(tag); + } + ; + } + ; + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeDoubleSize(1, this.doubleVal()); + size = size + CodedOutputStream.computeFloatSize(2, this.floatVal()); + size = size + CodedOutputStream.computeInt32Size(3, this.int32Val()); + size = size + CodedOutputStream.computeInt64Size(4, this.int64Val()); + size = size + CodedOutputStream.computeUInt32Size(5, this.uint32Val()); + size = size + CodedOutputStream.computeUInt64Size(6, this.uint64Val()); + size = size + CodedOutputStream.computeSInt32Size(7, this.sint32Val()); + size = size + CodedOutputStream.computeSInt64Size(8, this.sint64Val()); + size = size + CodedOutputStream.computeFixed32Size(9, this.fixed32Val()); + size = size + CodedOutputStream.computeFixed64Size(10, this.fixed64Val()); + size = size + CodedOutputStream.computeSFixed32Size(11, this.sfixed32Val()); + size = size + CodedOutputStream.computeSFixed64Size(12, this.sfixed64Val()); + size = size + CodedOutputStream.computeBoolSize(13, this.boolVal()); + size = size + CodedOutputStream.computeStringSize(14, this.stringVal()); + size = size + CodedOutputStream.computeBytesSize(15, this.bytesVal()); + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeDouble(1, this.doubleVal()); + output.writeFloat(2, this.floatVal()); + output.writeInt32(3, this.int32Val()); + output.writeInt64(4, this.int64Val()); + output.writeUInt32(5, this.uint32Val()); + output.writeUInt64(6, this.uint64Val()); + output.writeSInt32(7, this.sint32Val()); + output.writeSInt64(8, this.sint64Val()); + output.writeFixed32(9, this.fixed32Val()); + output.writeFixed64(10, this.fixed64Val()); + output.writeSFixed32(11, this.sfixed32Val()); + output.writeSFixed64(12, this.sfixed64Val()); + output.writeBool(13, this.boolVal()); + output.writeString(14, this.stringVal()); + output.writeBytes(15, this.bytesVal()); + } +} diff --git a/testers/grpc/java/generated-and-checked-in/com/example/grpc/Wallet.java b/testers/grpc/java/generated-and-checked-in/com/example/grpc/Wallet.java new file mode 100644 index 0000000000..52ce7c24d5 --- /dev/null +++ b/testers/grpc/java/generated-and-checked-in/com/example/grpc/Wallet.java @@ -0,0 +1,74 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public record Wallet(String walletId, String provider) { + public Wallet withWalletId(String walletId) { + return new Wallet(walletId, provider); + } + + public Wallet withProvider(String provider) { + return new Wallet(walletId, provider); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(Wallet value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public Wallet parse(InputStream stream) { + try { + return Wallet.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static Wallet parseFrom(CodedInputStream input) throws IOException { + String walletId = ""; + String provider = ""; + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + walletId = input.readString(); + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + provider = input.readString(); + } else { + input.skipField(tag); + } + ; + } + ; + return new Wallet(walletId, provider); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = size + CodedOutputStream.computeStringSize(1, this.walletId()); + size = size + CodedOutputStream.computeStringSize(2, this.provider()); + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeString(1, this.walletId()); + output.writeString(2, this.provider()); + } +} diff --git a/testers/grpc/java/generated-and-checked-in/com/example/grpc/WellKnownTypesMessage.java b/testers/grpc/java/generated-and-checked-in/com/example/grpc/WellKnownTypesMessage.java new file mode 100644 index 0000000000..f0ee16f7e5 --- /dev/null +++ b/testers/grpc/java/generated-and-checked-in/com/example/grpc/WellKnownTypesMessage.java @@ -0,0 +1,226 @@ +package com.example.grpc; + +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.WireFormat; +import io.grpc.MethodDescriptor.Marshaller; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.time.Duration; +import java.time.Instant; +import java.util.Optional; + +public record WellKnownTypesMessage( + Instant createdAt, + Duration ttl, + Optional nullableString, + Optional nullableInt, + Optional nullableBool) { + public WellKnownTypesMessage withCreatedAt(Instant createdAt) { + return new WellKnownTypesMessage(createdAt, ttl, nullableString, nullableInt, nullableBool); + } + + public WellKnownTypesMessage withTtl(Duration ttl) { + return new WellKnownTypesMessage(createdAt, ttl, nullableString, nullableInt, nullableBool); + } + + public WellKnownTypesMessage withNullableString(Optional nullableString) { + return new WellKnownTypesMessage(createdAt, ttl, nullableString, nullableInt, nullableBool); + } + + public WellKnownTypesMessage withNullableInt(Optional nullableInt) { + return new WellKnownTypesMessage(createdAt, ttl, nullableString, nullableInt, nullableBool); + } + + public WellKnownTypesMessage withNullableBool(Optional nullableBool) { + return new WellKnownTypesMessage(createdAt, ttl, nullableString, nullableInt, nullableBool); + } + + public static Marshaller MARSHALLER = + new Marshaller() { + @Override + public InputStream stream(WellKnownTypesMessage value) { + var bytes = new byte[value.getSerializedSize()]; + var cos = CodedOutputStream.newInstance(bytes); + try { + value.writeTo(cos); + cos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new ByteArrayInputStream(bytes); + } + + @Override + public WellKnownTypesMessage parse(InputStream stream) { + try { + return WellKnownTypesMessage.parseFrom(CodedInputStream.newInstance(stream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + public static WellKnownTypesMessage parseFrom(CodedInputStream input) throws IOException { + Instant createdAt = Instant.EPOCH; + Duration ttl = Duration.ZERO; + Optional nullableString = Optional.empty(); + Optional nullableInt = Optional.empty(); + Optional nullableBool = Optional.empty(); + while (!input.isAtEnd()) { + var tag = input.readTag(); + if (WireFormat.getTagFieldNumber(tag) == 1) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + var _tsSeconds = 0L; + var _tsNanos = 0; + while (!input.isAtEnd()) { + var _tsTag = input.readTag(); + if (WireFormat.getTagFieldNumber(_tsTag) == 1) { + _tsSeconds = input.readInt64(); + } else if (WireFormat.getTagFieldNumber(_tsTag) == 2) { + _tsNanos = input.readInt32(); + } else { + input.skipField(_tsTag); + } + ; + } + ; + createdAt = Instant.ofEpochSecond(_tsSeconds, (long) (_tsNanos)); + input.popLimit(_oldLimit); + ; + } else if (WireFormat.getTagFieldNumber(tag) == 2) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + var _durSeconds = 0L; + var _durNanos = 0; + while (!input.isAtEnd()) { + var _durTag = input.readTag(); + if (WireFormat.getTagFieldNumber(_durTag) == 1) { + _durSeconds = input.readInt64(); + } else if (WireFormat.getTagFieldNumber(_durTag) == 2) { + _durNanos = input.readInt32(); + } else { + input.skipField(_durTag); + } + ; + } + ; + ttl = Duration.ofSeconds(_durSeconds, (long) (_durNanos)); + input.popLimit(_oldLimit); + ; + } else if (WireFormat.getTagFieldNumber(tag) == 3) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + input.readTag(); + nullableString = Optional.of(input.readString()); + input.popLimit(_oldLimit); + ; + } else if (WireFormat.getTagFieldNumber(tag) == 4) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + input.readTag(); + nullableInt = Optional.of(input.readInt32()); + input.popLimit(_oldLimit); + ; + } else if (WireFormat.getTagFieldNumber(tag) == 5) { + var _length = input.readRawVarint32(); + var _oldLimit = input.pushLimit(_length); + input.readTag(); + nullableBool = Optional.of(input.readBool()); + input.popLimit(_oldLimit); + ; + } else { + input.skipField(tag); + } + ; + } + ; + return new WellKnownTypesMessage(createdAt, ttl, nullableString, nullableInt, nullableBool); + } + + public Integer getSerializedSize() { + Integer size = 0; + size = + size + + CodedOutputStream.computeTagSize(1) + + CodedOutputStream.computeUInt32SizeNoTag( + CodedOutputStream.computeInt64Size(1, this.createdAt().getEpochSecond()) + + CodedOutputStream.computeInt32Size(2, this.createdAt().getNano())) + + CodedOutputStream.computeInt64Size(1, this.createdAt().getEpochSecond()) + + CodedOutputStream.computeInt32Size(2, this.createdAt().getNano()); + size = + size + + CodedOutputStream.computeTagSize(2) + + CodedOutputStream.computeUInt32SizeNoTag( + CodedOutputStream.computeInt64Size(1, this.ttl().getSeconds()) + + CodedOutputStream.computeInt32Size(2, this.ttl().getNano())) + + CodedOutputStream.computeInt64Size(1, this.ttl().getSeconds()) + + CodedOutputStream.computeInt32Size(2, this.ttl().getNano()); + if (this.nullableString().isPresent()) { + var v = this.nullableString().get(); + size = + size + + CodedOutputStream.computeTagSize(3) + + CodedOutputStream.computeUInt32SizeNoTag(CodedOutputStream.computeStringSize(1, v)) + + CodedOutputStream.computeStringSize(1, v); + ; + } + if (this.nullableInt().isPresent()) { + var v = this.nullableInt().get(); + size = + size + + CodedOutputStream.computeTagSize(4) + + CodedOutputStream.computeUInt32SizeNoTag(CodedOutputStream.computeInt32Size(1, v)) + + CodedOutputStream.computeInt32Size(1, v); + ; + } + if (this.nullableBool().isPresent()) { + var v = this.nullableBool().get(); + size = + size + + CodedOutputStream.computeTagSize(5) + + CodedOutputStream.computeUInt32SizeNoTag(CodedOutputStream.computeBoolSize(1, v)) + + CodedOutputStream.computeBoolSize(1, v); + ; + } + return size; + } + + public void writeTo(CodedOutputStream output) throws IOException { + output.writeTag(1, 2); + output.writeUInt32NoTag( + CodedOutputStream.computeInt64Size(1, this.createdAt().getEpochSecond()) + + CodedOutputStream.computeInt32Size(2, this.createdAt().getNano())); + output.writeInt64(1, this.createdAt().getEpochSecond()); + output.writeInt32(2, this.createdAt().getNano()); + output.writeTag(2, 2); + output.writeUInt32NoTag( + CodedOutputStream.computeInt64Size(1, this.ttl().getSeconds()) + + CodedOutputStream.computeInt32Size(2, this.ttl().getNano())); + output.writeInt64(1, this.ttl().getSeconds()); + output.writeInt32(2, this.ttl().getNano()); + if (this.nullableString().isPresent()) { + var v = this.nullableString().get(); + output.writeTag(3, 2); + output.writeUInt32NoTag(CodedOutputStream.computeStringSize(1, v)); + output.writeString(1, v); + ; + } + if (this.nullableInt().isPresent()) { + var v = this.nullableInt().get(); + output.writeTag(4, 2); + output.writeUInt32NoTag(CodedOutputStream.computeInt32Size(1, v)); + output.writeInt32(1, v); + ; + } + if (this.nullableBool().isPresent()) { + var v = this.nullableBool().get(); + output.writeTag(5, 2); + output.writeUInt32NoTag(CodedOutputStream.computeBoolSize(1, v)); + output.writeBool(1, v); + ; + } + } +} diff --git a/testers/grpc/java/src/java/com/example/grpc/GrpcIntegrationTest.java b/testers/grpc/java/src/java/com/example/grpc/GrpcIntegrationTest.java new file mode 100644 index 0000000000..5fb9d58f5f --- /dev/null +++ b/testers/grpc/java/src/java/com/example/grpc/GrpcIntegrationTest.java @@ -0,0 +1,520 @@ +package com.example.grpc; + +import static org.junit.Assert.*; + +import com.google.protobuf.ByteString; +import io.grpc.ManagedChannel; +import io.grpc.Server; +import io.grpc.inprocess.InProcessChannelBuilder; +import io.grpc.inprocess.InProcessServerBuilder; +import java.time.Duration; +import java.time.Instant; +import java.util.*; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class GrpcIntegrationTest { + + private Server server; + private ManagedChannel channel; + private OrderServiceClient orderClient; + private EchoServiceClient echoClient; + + private final Customer testCustomer = + new Customer(CustomerId.valueOf("CUST-123"), "John Doe", "john@example.com"); + + @Before + public void setUp() throws Exception { + String serverName = InProcessServerBuilder.generateName(); + + OrderService orderImpl = + new OrderService() { + @Override + public GetCustomerResponse getCustomer(GetCustomerRequest request) { + return new GetCustomerResponse( + new Customer( + CustomerId.valueOf(request.customerId()), "John Doe", "john@example.com")); + } + + @Override + public CreateOrderResponse createOrder(CreateOrderRequest request) { + return new CreateOrderResponse( + request.order().orderId().unwrap(), OrderStatus.ORDER_STATUS_PENDING); + } + + @Override + public Iterator listOrders(ListOrdersRequest request) { + List updates = new ArrayList<>(); + updates.add( + new OrderUpdate( + "ORD-1", OrderStatus.ORDER_STATUS_PENDING, Instant.ofEpochSecond(1000, 500))); + updates.add( + new OrderUpdate( + "ORD-2", OrderStatus.ORDER_STATUS_SHIPPED, Instant.ofEpochSecond(2000, 1000))); + updates.add( + new OrderUpdate( + "ORD-3", OrderStatus.ORDER_STATUS_DELIVERED, Instant.ofEpochSecond(3000, 0))); + return updates.iterator(); + } + + @Override + public OrderSummary submitOrders(Iterator requests) { + throw new UnsupportedOperationException(); + } + + @Override + public Iterator chat(Iterator requests) { + throw new UnsupportedOperationException(); + } + }; + + EchoService echoImpl = + new EchoService() { + @Override + public ScalarTypes echoScalarTypes(ScalarTypes request) { + return request; + } + + @Override + public Customer echoCustomer(Customer request) { + return request; + } + + @Override + public Order echoOrder(Order request) { + return request; + } + + @Override + public Inventory echoInventory(Inventory request) { + return request; + } + + @Override + public Outer echoOuter(Outer request) { + return request; + } + + @Override + public OptionalFields echoOptionalFields(OptionalFields request) { + return request; + } + + @Override + public WellKnownTypesMessage echoWellKnownTypes(WellKnownTypesMessage request) { + return request; + } + + @Override + public PaymentMethod echoPaymentMethod(PaymentMethod request) { + return request; + } + + @Override + public Notification echoNotification(Notification request) { + return request; + } + }; + + server = + InProcessServerBuilder.forName(serverName) + .directExecutor() + .addService(new OrderServiceServer(orderImpl)) + .addService(new EchoServiceServer(echoImpl)) + .build() + .start(); + + channel = InProcessChannelBuilder.forName(serverName).directExecutor().build(); + orderClient = new OrderServiceClient(channel); + echoClient = new EchoServiceClient(channel); + } + + @After + public void tearDown() throws Exception { + channel.shutdownNow(); + server.shutdownNow(); + } + + // ---- gRPC service tests ---- + + @Test + public void testGetCustomer() { + GetCustomerResponse response = orderClient.getCustomer(new GetCustomerRequest("CUST-123")); + assertNotNull(response); + assertNotNull(response.customer()); + assertEquals("CUST-123", response.customer().customerId().unwrap()); + assertEquals("John Doe", response.customer().name()); + assertEquals("john@example.com", response.customer().email()); + } + + @Test + public void testCreateOrder() { + Order order = + new Order( + OrderId.valueOf("ORD-42"), + CustomerId.valueOf("CUST-1"), + 9999L, + Instant.ofEpochSecond(1700000000L, 123456789)); + + CreateOrderResponse response = orderClient.createOrder(new CreateOrderRequest(order)); + assertNotNull(response); + assertEquals("ORD-42", response.orderId()); + assertEquals(OrderStatus.ORDER_STATUS_PENDING, response.status()); + } + + @Test + public void testListOrders() { + Iterator updates = orderClient.listOrders(new ListOrdersRequest("CUST-123", 10)); + List results = new ArrayList<>(); + updates.forEachRemaining(results::add); + + assertEquals(3, results.size()); + assertEquals("ORD-1", results.get(0).orderId()); + assertEquals(OrderStatus.ORDER_STATUS_PENDING, results.get(0).status()); + assertEquals("ORD-2", results.get(1).orderId()); + assertEquals(OrderStatus.ORDER_STATUS_SHIPPED, results.get(1).status()); + assertEquals("ORD-3", results.get(2).orderId()); + assertEquals(OrderStatus.ORDER_STATUS_DELIVERED, results.get(2).status()); + } + + // ---- Echo round-trip tests ---- + + @Test + public void testEchoCustomer() { + Customer parsed = echoClient.echoCustomer(testCustomer); + assertEquals(testCustomer, parsed); + } + + @Test + public void testEchoOrder() { + Order order = + new Order( + OrderId.valueOf("ORD-1"), + CustomerId.valueOf("CUST-1"), + 5000L, + Instant.ofEpochSecond(1700000000L, 123456789)); + Order parsed = echoClient.echoOrder(order); + assertEquals(order.orderId(), parsed.orderId()); + assertEquals(order.customerId(), parsed.customerId()); + assertEquals(order.amountCents(), parsed.amountCents()); + assertEquals(order.createdAt(), parsed.createdAt()); + } + + // ---- Scalar types ---- + + @Test + public void testEchoScalarTypes() { + ScalarTypes scalars = + new ScalarTypes( + 3.14, + 2.71f, + 42, + 9876543210L, + 100, + 200L, + -50, + -100L, + 999, + 888L, + -777, + -666L, + true, + "hello world", + ByteString.copyFromUtf8("binary data")); + ScalarTypes parsed = echoClient.echoScalarTypes(scalars); + assertEquals(scalars.doubleVal(), parsed.doubleVal(), 0.0001); + assertEquals(scalars.floatVal(), parsed.floatVal(), 0.0001f); + assertEquals(scalars.int32Val(), parsed.int32Val()); + assertEquals(scalars.int64Val(), parsed.int64Val()); + assertEquals(scalars.uint32Val(), parsed.uint32Val()); + assertEquals(scalars.uint64Val(), parsed.uint64Val()); + assertEquals(scalars.sint32Val(), parsed.sint32Val()); + assertEquals(scalars.sint64Val(), parsed.sint64Val()); + assertEquals(scalars.fixed32Val(), parsed.fixed32Val()); + assertEquals(scalars.fixed64Val(), parsed.fixed64Val()); + assertEquals(scalars.sfixed32Val(), parsed.sfixed32Val()); + assertEquals(scalars.sfixed64Val(), parsed.sfixed64Val()); + assertEquals(scalars.boolVal(), parsed.boolVal()); + assertEquals(scalars.stringVal(), parsed.stringVal()); + assertEquals(scalars.bytesVal(), parsed.bytesVal()); + } + + // ---- Enum tests ---- + + @Test + public void testEnumToValueFromValueRoundTrip() { + for (OrderStatus status : OrderStatus.values()) { + Integer wireValue = status.toValue(); + OrderStatus back = OrderStatus.fromValue(wireValue); + assertEquals(status, back); + } + } + + @Test + public void testEnumForce() { + assertEquals(OrderStatus.ORDER_STATUS_PENDING, OrderStatus.force("ORDER_STATUS_PENDING")); + } + + @Test(expected = RuntimeException.class) + public void testEnumForceInvalid() { + OrderStatus.force("NONEXISTENT"); + } + + @Test(expected = IllegalArgumentException.class) + public void testEnumFromValueInvalid() { + OrderStatus.fromValue(999); + } + + @Test + public void testPriorityEnumRoundTrip() { + for (Priority p : Priority.values()) { + Integer wireValue = p.toValue(); + Priority back = Priority.fromValue(wireValue); + assertEquals(p, back); + } + } + + // ---- Optional fields ---- + + @Test + public void testEchoOptionalFieldsAllPresent() { + OptionalFields opt = + new OptionalFields(Optional.of("Alice"), Optional.of(30), Optional.of(testCustomer)); + OptionalFields parsed = echoClient.echoOptionalFields(opt); + assertEquals(Optional.of("Alice"), parsed.name()); + assertEquals(Optional.of(30), parsed.age()); + assertTrue(parsed.customer().isPresent()); + assertEquals("CUST-123", parsed.customer().get().customerId().unwrap()); + } + + @Test + public void testEchoOptionalFieldsAllEmpty() { + OptionalFields opt = new OptionalFields(Optional.empty(), Optional.empty(), Optional.empty()); + OptionalFields parsed = echoClient.echoOptionalFields(opt); + assertEquals(Optional.empty(), parsed.name()); + assertEquals(Optional.empty(), parsed.age()); + assertEquals(Optional.empty(), parsed.customer()); + } + + @Test + public void testEchoOptionalFieldsPartiallyPresent() { + OptionalFields opt = new OptionalFields(Optional.of("Bob"), Optional.empty(), Optional.empty()); + OptionalFields parsed = echoClient.echoOptionalFields(opt); + assertEquals(Optional.of("Bob"), parsed.name()); + assertEquals(Optional.empty(), parsed.age()); + assertEquals(Optional.empty(), parsed.customer()); + } + + // ---- Nested messages ---- + + @Test + public void testEchoOuter() { + Outer outer = new Outer("outer-name", new Inner(42, "inner-desc")); + Outer parsed = echoClient.echoOuter(outer); + assertEquals("outer-name", parsed.name()); + assertNotNull(parsed.inner()); + assertEquals(Integer.valueOf(42), parsed.inner().value()); + assertEquals("inner-desc", parsed.inner().description()); + } + + // ---- OneOf types ---- + + @Test + public void testEchoPaymentMethodCreditCard() { + CreditCard cc = new CreditCard("4111111111111111", "12/25", "123"); + PaymentMethodMethod method = new PaymentMethodMethod.CreditCardValue(cc); + PaymentMethod pm = new PaymentMethod("PAY-1", method); + PaymentMethod parsed = echoClient.echoPaymentMethod(pm); + assertEquals("PAY-1", parsed.id()); + assertTrue(parsed.method() instanceof PaymentMethodMethod.CreditCardValue); + PaymentMethodMethod.CreditCardValue ccv = (PaymentMethodMethod.CreditCardValue) parsed.method(); + assertEquals("4111111111111111", ccv.creditCard().cardNumber()); + assertEquals("12/25", ccv.creditCard().expiryDate()); + assertEquals("123", ccv.creditCard().cvv()); + } + + @Test + public void testEchoPaymentMethodBankTransfer() { + BankTransfer bt = new BankTransfer("123456789", "021000021"); + PaymentMethodMethod method = new PaymentMethodMethod.BankTransferValue(bt); + PaymentMethod pm = new PaymentMethod("PAY-2", method); + PaymentMethod parsed = echoClient.echoPaymentMethod(pm); + assertEquals("PAY-2", parsed.id()); + assertTrue(parsed.method() instanceof PaymentMethodMethod.BankTransferValue); + PaymentMethodMethod.BankTransferValue btv = + (PaymentMethodMethod.BankTransferValue) parsed.method(); + assertEquals("123456789", btv.bankTransfer().accountNumber()); + assertEquals("021000021", btv.bankTransfer().routingNumber()); + } + + @Test + public void testEchoPaymentMethodWallet() { + Wallet w = new Wallet("wallet-42", "Stripe"); + PaymentMethodMethod method = new PaymentMethodMethod.WalletValue(w); + PaymentMethod pm = new PaymentMethod("PAY-3", method); + PaymentMethod parsed = echoClient.echoPaymentMethod(pm); + assertEquals("PAY-3", parsed.id()); + assertTrue(parsed.method() instanceof PaymentMethodMethod.WalletValue); + PaymentMethodMethod.WalletValue wv = (PaymentMethodMethod.WalletValue) parsed.method(); + assertEquals("wallet-42", wv.wallet().walletId()); + assertEquals("Stripe", wv.wallet().provider()); + } + + @Test + public void testEchoNotificationWithEmailTarget() { + Notification notif = + new Notification( + "Hello!", Priority.PRIORITY_HIGH, new NotificationTarget.Email("user@example.com")); + Notification parsed = echoClient.echoNotification(notif); + assertEquals("Hello!", parsed.message()); + assertEquals(Priority.PRIORITY_HIGH, parsed.priority()); + assertTrue(parsed.target() instanceof NotificationTarget.Email); + assertEquals("user@example.com", ((NotificationTarget.Email) parsed.target()).email()); + } + + @Test + public void testEchoNotificationWithPhoneTarget() { + Notification notif = + new Notification( + "Alert", Priority.PRIORITY_CRITICAL, new NotificationTarget.Phone("+1234567890")); + Notification parsed = echoClient.echoNotification(notif); + assertEquals("Alert", parsed.message()); + assertEquals(Priority.PRIORITY_CRITICAL, parsed.priority()); + assertTrue(parsed.target() instanceof NotificationTarget.Phone); + assertEquals("+1234567890", ((NotificationTarget.Phone) parsed.target()).phone()); + } + + @Test + public void testEchoNotificationWithWebhookTarget() { + Notification notif = + new Notification( + "Event", + Priority.PRIORITY_LOW, + new NotificationTarget.WebhookUrl("https://hooks.example.com/abc")); + Notification parsed = echoClient.echoNotification(notif); + assertEquals("Event", parsed.message()); + assertEquals(Priority.PRIORITY_LOW, parsed.priority()); + assertTrue(parsed.target() instanceof NotificationTarget.WebhookUrl); + assertEquals( + "https://hooks.example.com/abc", + ((NotificationTarget.WebhookUrl) parsed.target()).webhookUrl()); + } + + // ---- Collections ---- + + @Test + public void testEchoInventory() { + List productIds = List.of("PROD-1", "PROD-2", "PROD-3"); + Map stockCounts = new LinkedHashMap<>(); + stockCounts.put("PROD-1", 100); + stockCounts.put("PROD-2", 200); + stockCounts.put("PROD-3", 0); + List orders = + List.of( + new Order( + OrderId.valueOf("ORD-1"), + CustomerId.valueOf("CUST-1"), + 1000L, + Instant.ofEpochSecond(1000, 0)), + new Order( + OrderId.valueOf("ORD-2"), + CustomerId.valueOf("CUST-2"), + 2000L, + Instant.ofEpochSecond(2000, 0))); + + Inventory inventory = new Inventory("WH-1", productIds, stockCounts, orders); + Inventory parsed = echoClient.echoInventory(inventory); + assertEquals("WH-1", parsed.warehouseId()); + assertEquals(3, parsed.productIds().size()); + assertEquals("PROD-1", parsed.productIds().get(0)); + assertEquals("PROD-2", parsed.productIds().get(1)); + assertEquals("PROD-3", parsed.productIds().get(2)); + assertEquals(3, parsed.stockCounts().size()); + assertEquals(Integer.valueOf(100), parsed.stockCounts().get("PROD-1")); + assertEquals(Integer.valueOf(200), parsed.stockCounts().get("PROD-2")); + assertEquals(Integer.valueOf(0), parsed.stockCounts().get("PROD-3")); + assertEquals(2, parsed.recentOrders().size()); + assertEquals("ORD-1", parsed.recentOrders().get(0).orderId().unwrap()); + assertEquals("ORD-2", parsed.recentOrders().get(1).orderId().unwrap()); + } + + @Test + public void testEchoInventoryEmptyCollections() { + Inventory inventory = new Inventory("WH-EMPTY", List.of(), Map.of(), List.of()); + Inventory parsed = echoClient.echoInventory(inventory); + assertEquals("WH-EMPTY", parsed.warehouseId()); + assertTrue(parsed.productIds().isEmpty()); + assertTrue(parsed.stockCounts().isEmpty()); + assertTrue(parsed.recentOrders().isEmpty()); + } + + // ---- Well-known types ---- + + @Test + public void testEchoWellKnownTypes() { + WellKnownTypesMessage msg = + new WellKnownTypesMessage( + Instant.ofEpochSecond(1700000000L, 123456789), + Duration.ofSeconds(3600, 500000000), + Optional.of("hello"), + Optional.of(42), + Optional.of(true)); + WellKnownTypesMessage parsed = echoClient.echoWellKnownTypes(msg); + assertEquals(msg.createdAt(), parsed.createdAt()); + assertEquals(msg.ttl(), parsed.ttl()); + assertEquals(Optional.of("hello"), parsed.nullableString()); + assertEquals(Optional.of(42), parsed.nullableInt()); + assertEquals(Optional.of(true), parsed.nullableBool()); + } + + // ---- Wrapper ID types ---- + + @Test + public void testCustomerIdValueOf() { + CustomerId id = CustomerId.valueOf("abc"); + assertEquals("abc", id.unwrap()); + assertEquals("abc", id.toString()); + } + + @Test + public void testOrderIdValueOf() { + OrderId id = OrderId.valueOf("ORD-1"); + assertEquals("ORD-1", id.unwrap()); + assertEquals("ORD-1", id.toString()); + } + + // ---- With methods ---- + + @Test + public void testCustomerWithMethods() { + Customer updated = testCustomer.withName("Jane Doe"); + assertEquals("Jane Doe", updated.name()); + assertEquals(testCustomer.customerId(), updated.customerId()); + assertEquals(testCustomer.email(), updated.email()); + } + + @Test + public void testOrderWithMethods() { + Order order = + new Order( + OrderId.valueOf("ORD-1"), + CustomerId.valueOf("CUST-1"), + 1000L, + Instant.ofEpochSecond(1000, 0)); + Order updated = order.withAmountCents(2000L); + assertEquals(Long.valueOf(2000L), updated.amountCents()); + assertEquals(order.orderId(), updated.orderId()); + } + + // ---- Echo with empty strings ---- + + @Test + public void testEchoCustomerEmptyStrings() { + Customer empty = new Customer(CustomerId.valueOf(""), "", ""); + Customer parsed = echoClient.echoCustomer(empty); + assertEquals("", parsed.customerId().unwrap()); + assertEquals("", parsed.name()); + assertEquals("", parsed.email()); + } +} diff --git a/testers/grpc/kotlin-quarkus/build.gradle.kts b/testers/grpc/kotlin-quarkus/build.gradle.kts new file mode 100644 index 0000000000..eaf802f16b --- /dev/null +++ b/testers/grpc/kotlin-quarkus/build.gradle.kts @@ -0,0 +1,40 @@ +plugins { + kotlin("jvm") +} + +repositories { + mavenCentral() +} + +dependencies { + implementation(project(":foundations-jdbc")) + implementation("com.google.protobuf:protobuf-java:4.29.3") + implementation("io.grpc:grpc-netty-shaded:1.69.0") + implementation("io.grpc:grpc-protobuf:1.69.0") + implementation("io.grpc:grpc-stub:1.69.0") + implementation("io.smallrye.reactive:mutiny:2.7.0") + implementation("io.quarkus:quarkus-grpc:3.17.2") + + testImplementation("io.grpc:grpc-testing:1.69.0") + testImplementation("io.grpc:grpc-inprocess:1.69.0") + testImplementation("junit:junit:4.13.2") +} + +sourceSets { + main { + kotlin { + srcDir("generated-and-checked-in") + srcDir("src/kotlin") + } + } + test { + kotlin { + srcDir("src/test/kotlin") + } + } +} + + +tasks.test { + useJUnit() +} diff --git a/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/BankTransfer.kt b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/BankTransfer.kt new file mode 100644 index 0000000000..89fdb44fe7 --- /dev/null +++ b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/BankTransfer.kt @@ -0,0 +1,65 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +data class BankTransfer( + val accountNumber: kotlin.String, + val routingNumber: kotlin.String +) { + fun getSerializedSize(): Int { + var size: Int = 0 + size = size + CodedOutputStream.computeStringSize(1, this.accountNumber) + size = size + CodedOutputStream.computeStringSize(2, this.routingNumber) + return size + } + + @Throws(IOException::class) + fun writeTo(output: CodedOutputStream) { + output.writeString(1, this.accountNumber) + output.writeString(2, this.routingNumber) + } + + companion object { + val MARSHALLER: Marshaller = + object : Marshaller { + override fun stream(value: BankTransfer): InputStream { + val bytes = ByteArray(value.getSerializedSize()) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch (e: IOException) { + throw RuntimeException(e) + } + return ByteArrayInputStream(bytes) + } + override fun parse(stream: InputStream): BankTransfer { + try { + return BankTransfer.parseFrom(CodedInputStream.newInstance(stream)) + } catch (e: IOException) { + throw RuntimeException(e) + } + } + } + + @Throws(IOException::class) + fun parseFrom(input: CodedInputStream): BankTransfer { + var accountNumber: kotlin.String = "" + var routingNumber: kotlin.String = "" + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { accountNumber = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 2) { routingNumber = input.readString() } + else { input.skipField(tag) } + } + return BankTransfer(accountNumber, routingNumber) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/ChatMessage.kt b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/ChatMessage.kt new file mode 100644 index 0000000000..f751ee332c --- /dev/null +++ b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/ChatMessage.kt @@ -0,0 +1,85 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException +import java.time.Instant + +data class ChatMessage( + val sender: kotlin.String, + val content: kotlin.String, + val sentAt: Instant +) { + fun getSerializedSize(): Int { + var size: Int = 0 + size = size + CodedOutputStream.computeStringSize(1, this.sender) + size = size + CodedOutputStream.computeStringSize(2, this.content) + size = size + CodedOutputStream.computeTagSize(3) + CodedOutputStream.computeUInt32SizeNoTag(CodedOutputStream.computeInt64Size(1, this.sentAt.getEpochSecond()) + CodedOutputStream.computeInt32Size(2, this.sentAt.getNano())) + CodedOutputStream.computeInt64Size(1, this.sentAt.getEpochSecond()) + CodedOutputStream.computeInt32Size(2, this.sentAt.getNano()) + return size + } + + @Throws(IOException::class) + fun writeTo(output: CodedOutputStream) { + output.writeString(1, this.sender) + output.writeString(2, this.content) + output.writeTag(3, 2) + output.writeUInt32NoTag(CodedOutputStream.computeInt64Size(1, this.sentAt.getEpochSecond()) + CodedOutputStream.computeInt32Size(2, this.sentAt.getNano())) + output.writeInt64(1, this.sentAt.getEpochSecond()) + output.writeInt32(2, this.sentAt.getNano()) + } + + companion object { + val MARSHALLER: Marshaller = + object : Marshaller { + override fun stream(value: ChatMessage): InputStream { + val bytes = ByteArray(value.getSerializedSize()) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch (e: IOException) { + throw RuntimeException(e) + } + return ByteArrayInputStream(bytes) + } + override fun parse(stream: InputStream): ChatMessage { + try { + return ChatMessage.parseFrom(CodedInputStream.newInstance(stream)) + } catch (e: IOException) { + throw RuntimeException(e) + } + } + } + + @Throws(IOException::class) + fun parseFrom(input: CodedInputStream): ChatMessage { + var sender: kotlin.String = "" + var content: kotlin.String = "" + var sentAt: Instant = Instant.EPOCH + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { sender = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 2) { content = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 3) { val _length = input.readRawVarint32(); + val _oldLimit = input.pushLimit(_length); + var _tsSeconds = 0L; + var _tsNanos = 0; + while (!input.isAtEnd()) { + val _tsTag = input.readTag() + if (WireFormat.getTagFieldNumber(_tsTag) == 1) { _tsSeconds = input.readInt64() } + else if (WireFormat.getTagFieldNumber(_tsTag) == 2) { _tsNanos = input.readInt32() } + else { input.skipField(_tsTag) } + }; + sentAt = Instant.ofEpochSecond(_tsSeconds, _tsNanos.toLong()); + input.popLimit(_oldLimit); } + else { input.skipField(tag) } + } + return ChatMessage(sender, content, sentAt) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/CreateOrderRequest.kt b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/CreateOrderRequest.kt new file mode 100644 index 0000000000..8ca493eca0 --- /dev/null +++ b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/CreateOrderRequest.kt @@ -0,0 +1,67 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +data class CreateOrderRequest(val order: Order?) { + fun getSerializedSize(): Int { + var size: Int = 0 + if ((this.order != null)) { + size = size + CodedOutputStream.computeTagSize(1) + CodedOutputStream.computeUInt32SizeNoTag(this.order.getSerializedSize()) + this.order.getSerializedSize() + } + return size + } + + @Throws(IOException::class) + fun writeTo(output: CodedOutputStream) { + if ((this.order != null)) { + output.writeTag(1, 2); + output.writeUInt32NoTag(this.order.getSerializedSize()); + this.order.writeTo(output); + } + } + + companion object { + val MARSHALLER: Marshaller = + object : Marshaller { + override fun stream(value: CreateOrderRequest): InputStream { + val bytes = ByteArray(value.getSerializedSize()) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch (e: IOException) { + throw RuntimeException(e) + } + return ByteArrayInputStream(bytes) + } + override fun parse(stream: InputStream): CreateOrderRequest { + try { + return CreateOrderRequest.parseFrom(CodedInputStream.newInstance(stream)) + } catch (e: IOException) { + throw RuntimeException(e) + } + } + } + + @Throws(IOException::class) + fun parseFrom(input: CodedInputStream): CreateOrderRequest { + var order: Order? = null + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { val _length = input.readRawVarint32(); + val _oldLimit = input.pushLimit(_length); + order = Order.parseFrom(input); + input.popLimit(_oldLimit); } + else { input.skipField(tag) } + } + return CreateOrderRequest(order) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/CreateOrderResponse.kt b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/CreateOrderResponse.kt new file mode 100644 index 0000000000..da64b08eb1 --- /dev/null +++ b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/CreateOrderResponse.kt @@ -0,0 +1,65 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +data class CreateOrderResponse( + val orderId: kotlin.String, + val status: OrderStatus +) { + fun getSerializedSize(): Int { + var size: Int = 0 + size = size + CodedOutputStream.computeStringSize(1, this.orderId) + size = size + CodedOutputStream.computeEnumSize(2, this.status.toValue()) + return size + } + + @Throws(IOException::class) + fun writeTo(output: CodedOutputStream) { + output.writeString(1, this.orderId) + output.writeEnum(2, this.status.toValue()) + } + + companion object { + val MARSHALLER: Marshaller = + object : Marshaller { + override fun stream(value: CreateOrderResponse): InputStream { + val bytes = ByteArray(value.getSerializedSize()) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch (e: IOException) { + throw RuntimeException(e) + } + return ByteArrayInputStream(bytes) + } + override fun parse(stream: InputStream): CreateOrderResponse { + try { + return CreateOrderResponse.parseFrom(CodedInputStream.newInstance(stream)) + } catch (e: IOException) { + throw RuntimeException(e) + } + } + } + + @Throws(IOException::class) + fun parseFrom(input: CodedInputStream): CreateOrderResponse { + var orderId: kotlin.String = "" + var status: OrderStatus = OrderStatus.fromValue(0) + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { orderId = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 2) { status = OrderStatus.fromValue(input.readEnum()) } + else { input.skipField(tag) } + } + return CreateOrderResponse(orderId, status) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/CreditCard.kt b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/CreditCard.kt new file mode 100644 index 0000000000..bcdeb4e3bb --- /dev/null +++ b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/CreditCard.kt @@ -0,0 +1,70 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +data class CreditCard( + val cardNumber: kotlin.String, + val expiryDate: kotlin.String, + val cvv: kotlin.String +) { + fun getSerializedSize(): Int { + var size: Int = 0 + size = size + CodedOutputStream.computeStringSize(1, this.cardNumber) + size = size + CodedOutputStream.computeStringSize(2, this.expiryDate) + size = size + CodedOutputStream.computeStringSize(3, this.cvv) + return size + } + + @Throws(IOException::class) + fun writeTo(output: CodedOutputStream) { + output.writeString(1, this.cardNumber) + output.writeString(2, this.expiryDate) + output.writeString(3, this.cvv) + } + + companion object { + val MARSHALLER: Marshaller = + object : Marshaller { + override fun stream(value: CreditCard): InputStream { + val bytes = ByteArray(value.getSerializedSize()) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch (e: IOException) { + throw RuntimeException(e) + } + return ByteArrayInputStream(bytes) + } + override fun parse(stream: InputStream): CreditCard { + try { + return CreditCard.parseFrom(CodedInputStream.newInstance(stream)) + } catch (e: IOException) { + throw RuntimeException(e) + } + } + } + + @Throws(IOException::class) + fun parseFrom(input: CodedInputStream): CreditCard { + var cardNumber: kotlin.String = "" + var expiryDate: kotlin.String = "" + var cvv: kotlin.String = "" + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { cardNumber = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 2) { expiryDate = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 3) { cvv = input.readString() } + else { input.skipField(tag) } + } + return CreditCard(cardNumber, expiryDate, cvv) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/Customer.kt b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/Customer.kt new file mode 100644 index 0000000000..f45a363edb --- /dev/null +++ b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/Customer.kt @@ -0,0 +1,70 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +data class Customer( + val customerId: CustomerId, + val name: kotlin.String, + val email: kotlin.String +) { + fun getSerializedSize(): Int { + var size: Int = 0 + size = size + CodedOutputStream.computeStringSize(1, this.customerId.unwrap()) + size = size + CodedOutputStream.computeStringSize(2, this.name) + size = size + CodedOutputStream.computeStringSize(3, this.email) + return size + } + + @Throws(IOException::class) + fun writeTo(output: CodedOutputStream) { + output.writeString(1, this.customerId.unwrap()) + output.writeString(2, this.name) + output.writeString(3, this.email) + } + + companion object { + val MARSHALLER: Marshaller = + object : Marshaller { + override fun stream(value: Customer): InputStream { + val bytes = ByteArray(value.getSerializedSize()) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch (e: IOException) { + throw RuntimeException(e) + } + return ByteArrayInputStream(bytes) + } + override fun parse(stream: InputStream): Customer { + try { + return Customer.parseFrom(CodedInputStream.newInstance(stream)) + } catch (e: IOException) { + throw RuntimeException(e) + } + } + } + + @Throws(IOException::class) + fun parseFrom(input: CodedInputStream): Customer { + var customerId: CustomerId = CustomerId.valueOf("") + var name: kotlin.String = "" + var email: kotlin.String = "" + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { customerId = CustomerId.valueOf(input.readString()) } + else if (WireFormat.getTagFieldNumber(tag) == 2) { name = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 3) { email = input.readString() } + else { input.skipField(tag) } + } + return Customer(customerId, name, email) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/CustomerId.kt b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/CustomerId.kt new file mode 100644 index 0000000000..2d4fdc014a --- /dev/null +++ b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/CustomerId.kt @@ -0,0 +1,22 @@ +package com.example.grpc + + + +/** Wrapper type for typr.grpc.GrpcCodegen$$$Lambda/0x00007fc001118400@1c9b0314 */ +data class CustomerId(val value: kotlin.String) { + /** Get the underlying value */ + fun unwrap(): kotlin.String { + return this.value + } + + override fun toString(): kotlin.String { + return value + } + + companion object { + /** Create a CustomerId from a raw value */ + fun valueOf(v: kotlin.String): CustomerId { + return CustomerId(v) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/EchoService.kt b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/EchoService.kt new file mode 100644 index 0000000000..c3fa2d642f --- /dev/null +++ b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/EchoService.kt @@ -0,0 +1,24 @@ +package com.example.grpc + +import io.smallrye.mutiny.Uni + +/** Clean service interface for EchoService gRPC service */ +interface EchoService { + abstract fun echoCustomer(request: Customer): Uni + + abstract fun echoInventory(request: Inventory): Uni + + abstract fun echoNotification(request: Notification): Uni + + abstract fun echoOptionalFields(request: OptionalFields): Uni + + abstract fun echoOrder(request: Order): Uni + + abstract fun echoOuter(request: Outer): Uni + + abstract fun echoPaymentMethod(request: PaymentMethod): Uni + + abstract fun echoScalarTypes(request: ScalarTypes): Uni + + abstract fun echoWellKnownTypes(request: WellKnownTypesMessage): Uni +} \ No newline at end of file diff --git a/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/EchoServiceClient.kt b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/EchoServiceClient.kt new file mode 100644 index 0000000000..acd1840276 --- /dev/null +++ b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/EchoServiceClient.kt @@ -0,0 +1,68 @@ +package com.example.grpc + +import io.grpc.CallOptions +import io.grpc.Channel +import io.grpc.MethodDescriptor +import io.grpc.MethodDescriptor.MethodType +import io.grpc.stub.ClientCalls +import io.quarkus.grpc.GrpcClient +import io.smallrye.mutiny.Uni + +/** gRPC client wrapper for EchoService - wraps Channel with clean types */ +data class EchoServiceClient(@field:GrpcClient("EchoService") val channel: Channel) : EchoService { + override fun echoCustomer(request: Customer): Uni { + return Uni.createFrom().item({ ClientCalls.blockingUnaryCall(channel, EchoServiceClient.ECHO_CUSTOMER, CallOptions.DEFAULT, request) }) + } + + override fun echoInventory(request: Inventory): Uni { + return Uni.createFrom().item({ ClientCalls.blockingUnaryCall(channel, EchoServiceClient.ECHO_INVENTORY, CallOptions.DEFAULT, request) }) + } + + override fun echoNotification(request: Notification): Uni { + return Uni.createFrom().item({ ClientCalls.blockingUnaryCall(channel, EchoServiceClient.ECHO_NOTIFICATION, CallOptions.DEFAULT, request) }) + } + + override fun echoOptionalFields(request: OptionalFields): Uni { + return Uni.createFrom().item({ ClientCalls.blockingUnaryCall(channel, EchoServiceClient.ECHO_OPTIONAL_FIELDS, CallOptions.DEFAULT, request) }) + } + + override fun echoOrder(request: Order): Uni { + return Uni.createFrom().item({ ClientCalls.blockingUnaryCall(channel, EchoServiceClient.ECHO_ORDER, CallOptions.DEFAULT, request) }) + } + + override fun echoOuter(request: Outer): Uni { + return Uni.createFrom().item({ ClientCalls.blockingUnaryCall(channel, EchoServiceClient.ECHO_OUTER, CallOptions.DEFAULT, request) }) + } + + override fun echoPaymentMethod(request: PaymentMethod): Uni { + return Uni.createFrom().item({ ClientCalls.blockingUnaryCall(channel, EchoServiceClient.ECHO_PAYMENT_METHOD, CallOptions.DEFAULT, request) }) + } + + override fun echoScalarTypes(request: ScalarTypes): Uni { + return Uni.createFrom().item({ ClientCalls.blockingUnaryCall(channel, EchoServiceClient.ECHO_SCALAR_TYPES, CallOptions.DEFAULT, request) }) + } + + override fun echoWellKnownTypes(request: WellKnownTypesMessage): Uni { + return Uni.createFrom().item({ ClientCalls.blockingUnaryCall(channel, EchoServiceClient.ECHO_WELL_KNOWN_TYPES, CallOptions.DEFAULT, request) }) + } + + companion object { + val ECHO_CUSTOMER: MethodDescriptor = MethodDescriptor.newBuilder(Customer.MARSHALLER, Customer.MARSHALLER).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoCustomer").build() + + val ECHO_INVENTORY: MethodDescriptor = MethodDescriptor.newBuilder(Inventory.MARSHALLER, Inventory.MARSHALLER).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoInventory").build() + + val ECHO_NOTIFICATION: MethodDescriptor = MethodDescriptor.newBuilder(Notification.MARSHALLER, Notification.MARSHALLER).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoNotification").build() + + val ECHO_OPTIONAL_FIELDS: MethodDescriptor = MethodDescriptor.newBuilder(OptionalFields.MARSHALLER, OptionalFields.MARSHALLER).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoOptionalFields").build() + + val ECHO_ORDER: MethodDescriptor = MethodDescriptor.newBuilder(Order.MARSHALLER, Order.MARSHALLER).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoOrder").build() + + val ECHO_OUTER: MethodDescriptor = MethodDescriptor.newBuilder(Outer.MARSHALLER, Outer.MARSHALLER).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoOuter").build() + + val ECHO_PAYMENT_METHOD: MethodDescriptor = MethodDescriptor.newBuilder(PaymentMethod.MARSHALLER, PaymentMethod.MARSHALLER).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoPaymentMethod").build() + + val ECHO_SCALAR_TYPES: MethodDescriptor = MethodDescriptor.newBuilder(ScalarTypes.MARSHALLER, ScalarTypes.MARSHALLER).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoScalarTypes").build() + + val ECHO_WELL_KNOWN_TYPES: MethodDescriptor = MethodDescriptor.newBuilder(WellKnownTypesMessage.MARSHALLER, WellKnownTypesMessage.MARSHALLER).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoWellKnownTypes").build() + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/EchoServiceServer.kt b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/EchoServiceServer.kt new file mode 100644 index 0000000000..ecb46c9cb9 --- /dev/null +++ b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/EchoServiceServer.kt @@ -0,0 +1,45 @@ +package com.example.grpc + +import io.grpc.BindableService +import io.grpc.MethodDescriptor +import io.grpc.MethodDescriptor.MethodType +import io.grpc.ServerServiceDefinition +import io.grpc.stub.ServerCalls +import io.quarkus.grpc.GrpcService +import jakarta.inject.Singleton + +/** gRPC server adapter for EchoService - delegates to clean service interface */ +data class EchoServiceServer(val delegate: EchoService) : BindableService { + override fun bindService(): ServerServiceDefinition { + return ServerServiceDefinition.builder("testgrpc.EchoService").addMethod(EchoServiceServer.ECHO_SCALAR_TYPES, ServerCalls.asyncUnaryCall({ request, responseObserver -> delegate.echoScalarTypes(request).subscribe().with({ response -> responseObserver.onNext(response) + responseObserver.onCompleted() }, { error -> responseObserver.onError(error) }) })).addMethod(EchoServiceServer.ECHO_CUSTOMER, ServerCalls.asyncUnaryCall({ request, responseObserver -> delegate.echoCustomer(request).subscribe().with({ response -> responseObserver.onNext(response) + responseObserver.onCompleted() }, { error -> responseObserver.onError(error) }) })).addMethod(EchoServiceServer.ECHO_ORDER, ServerCalls.asyncUnaryCall({ request, responseObserver -> delegate.echoOrder(request).subscribe().with({ response -> responseObserver.onNext(response) + responseObserver.onCompleted() }, { error -> responseObserver.onError(error) }) })).addMethod(EchoServiceServer.ECHO_INVENTORY, ServerCalls.asyncUnaryCall({ request, responseObserver -> delegate.echoInventory(request).subscribe().with({ response -> responseObserver.onNext(response) + responseObserver.onCompleted() }, { error -> responseObserver.onError(error) }) })).addMethod(EchoServiceServer.ECHO_OUTER, ServerCalls.asyncUnaryCall({ request, responseObserver -> delegate.echoOuter(request).subscribe().with({ response -> responseObserver.onNext(response) + responseObserver.onCompleted() }, { error -> responseObserver.onError(error) }) })).addMethod(EchoServiceServer.ECHO_OPTIONAL_FIELDS, ServerCalls.asyncUnaryCall({ request, responseObserver -> delegate.echoOptionalFields(request).subscribe().with({ response -> responseObserver.onNext(response) + responseObserver.onCompleted() }, { error -> responseObserver.onError(error) }) })).addMethod(EchoServiceServer.ECHO_WELL_KNOWN_TYPES, ServerCalls.asyncUnaryCall({ request, responseObserver -> delegate.echoWellKnownTypes(request).subscribe().with({ response -> responseObserver.onNext(response) + responseObserver.onCompleted() }, { error -> responseObserver.onError(error) }) })).addMethod(EchoServiceServer.ECHO_PAYMENT_METHOD, ServerCalls.asyncUnaryCall({ request, responseObserver -> delegate.echoPaymentMethod(request).subscribe().with({ response -> responseObserver.onNext(response) + responseObserver.onCompleted() }, { error -> responseObserver.onError(error) }) })).addMethod(EchoServiceServer.ECHO_NOTIFICATION, ServerCalls.asyncUnaryCall({ request, responseObserver -> delegate.echoNotification(request).subscribe().with({ response -> responseObserver.onNext(response) + responseObserver.onCompleted() }, { error -> responseObserver.onError(error) }) })).build() + } + + companion object { + val ECHO_CUSTOMER: MethodDescriptor = MethodDescriptor.newBuilder(Customer.MARSHALLER, Customer.MARSHALLER).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoCustomer").build() + + val ECHO_INVENTORY: MethodDescriptor = MethodDescriptor.newBuilder(Inventory.MARSHALLER, Inventory.MARSHALLER).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoInventory").build() + + val ECHO_NOTIFICATION: MethodDescriptor = MethodDescriptor.newBuilder(Notification.MARSHALLER, Notification.MARSHALLER).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoNotification").build() + + val ECHO_OPTIONAL_FIELDS: MethodDescriptor = MethodDescriptor.newBuilder(OptionalFields.MARSHALLER, OptionalFields.MARSHALLER).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoOptionalFields").build() + + val ECHO_ORDER: MethodDescriptor = MethodDescriptor.newBuilder(Order.MARSHALLER, Order.MARSHALLER).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoOrder").build() + + val ECHO_OUTER: MethodDescriptor = MethodDescriptor.newBuilder(Outer.MARSHALLER, Outer.MARSHALLER).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoOuter").build() + + val ECHO_PAYMENT_METHOD: MethodDescriptor = MethodDescriptor.newBuilder(PaymentMethod.MARSHALLER, PaymentMethod.MARSHALLER).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoPaymentMethod").build() + + val ECHO_SCALAR_TYPES: MethodDescriptor = MethodDescriptor.newBuilder(ScalarTypes.MARSHALLER, ScalarTypes.MARSHALLER).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoScalarTypes").build() + + val ECHO_WELL_KNOWN_TYPES: MethodDescriptor = MethodDescriptor.newBuilder(WellKnownTypesMessage.MARSHALLER, WellKnownTypesMessage.MARSHALLER).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoWellKnownTypes").build() + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/GetCustomerRequest.kt b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/GetCustomerRequest.kt new file mode 100644 index 0000000000..60593bbfc7 --- /dev/null +++ b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/GetCustomerRequest.kt @@ -0,0 +1,58 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +data class GetCustomerRequest(val customerId: kotlin.String) { + fun getSerializedSize(): Int { + var size: Int = 0 + size = size + CodedOutputStream.computeStringSize(1, this.customerId) + return size + } + + @Throws(IOException::class) + fun writeTo(output: CodedOutputStream) { + output.writeString(1, this.customerId) + } + + companion object { + val MARSHALLER: Marshaller = + object : Marshaller { + override fun stream(value: GetCustomerRequest): InputStream { + val bytes = ByteArray(value.getSerializedSize()) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch (e: IOException) { + throw RuntimeException(e) + } + return ByteArrayInputStream(bytes) + } + override fun parse(stream: InputStream): GetCustomerRequest { + try { + return GetCustomerRequest.parseFrom(CodedInputStream.newInstance(stream)) + } catch (e: IOException) { + throw RuntimeException(e) + } + } + } + + @Throws(IOException::class) + fun parseFrom(input: CodedInputStream): GetCustomerRequest { + var customerId: kotlin.String = "" + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { customerId = input.readString() } + else { input.skipField(tag) } + } + return GetCustomerRequest(customerId) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/GetCustomerResponse.kt b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/GetCustomerResponse.kt new file mode 100644 index 0000000000..325632d08f --- /dev/null +++ b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/GetCustomerResponse.kt @@ -0,0 +1,67 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +data class GetCustomerResponse(val customer: Customer?) { + fun getSerializedSize(): Int { + var size: Int = 0 + if ((this.customer != null)) { + size = size + CodedOutputStream.computeTagSize(1) + CodedOutputStream.computeUInt32SizeNoTag(this.customer.getSerializedSize()) + this.customer.getSerializedSize() + } + return size + } + + @Throws(IOException::class) + fun writeTo(output: CodedOutputStream) { + if ((this.customer != null)) { + output.writeTag(1, 2); + output.writeUInt32NoTag(this.customer.getSerializedSize()); + this.customer.writeTo(output); + } + } + + companion object { + val MARSHALLER: Marshaller = + object : Marshaller { + override fun stream(value: GetCustomerResponse): InputStream { + val bytes = ByteArray(value.getSerializedSize()) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch (e: IOException) { + throw RuntimeException(e) + } + return ByteArrayInputStream(bytes) + } + override fun parse(stream: InputStream): GetCustomerResponse { + try { + return GetCustomerResponse.parseFrom(CodedInputStream.newInstance(stream)) + } catch (e: IOException) { + throw RuntimeException(e) + } + } + } + + @Throws(IOException::class) + fun parseFrom(input: CodedInputStream): GetCustomerResponse { + var customer: Customer? = null + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { val _length = input.readRawVarint32(); + val _oldLimit = input.pushLimit(_length); + customer = Customer.parseFrom(input); + input.popLimit(_oldLimit); } + else { input.skipField(tag) } + } + return GetCustomerResponse(customer) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/Inner.kt b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/Inner.kt new file mode 100644 index 0000000000..4665ed427f --- /dev/null +++ b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/Inner.kt @@ -0,0 +1,65 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +data class Inner( + val value: Int, + val description: kotlin.String +) { + fun getSerializedSize(): Int { + var size: Int = 0 + size = size + CodedOutputStream.computeInt32Size(1, this.value) + size = size + CodedOutputStream.computeStringSize(2, this.description) + return size + } + + @Throws(IOException::class) + fun writeTo(output: CodedOutputStream) { + output.writeInt32(1, this.value) + output.writeString(2, this.description) + } + + companion object { + val MARSHALLER: Marshaller = + object : Marshaller { + override fun stream(value: Inner): InputStream { + val bytes = ByteArray(value.getSerializedSize()) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch (e: IOException) { + throw RuntimeException(e) + } + return ByteArrayInputStream(bytes) + } + override fun parse(stream: InputStream): Inner { + try { + return Inner.parseFrom(CodedInputStream.newInstance(stream)) + } catch (e: IOException) { + throw RuntimeException(e) + } + } + } + + @Throws(IOException::class) + fun parseFrom(input: CodedInputStream): Inner { + var value: Int = 0 + var description: kotlin.String = "" + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { value = input.readInt32() } + else if (WireFormat.getTagFieldNumber(tag) == 2) { description = input.readString() } + else { input.skipField(tag) } + } + return Inner(value, description) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/Inventory.kt b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/Inventory.kt new file mode 100644 index 0000000000..7c44f2ee04 --- /dev/null +++ b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/Inventory.kt @@ -0,0 +1,110 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException +import java.util.ArrayList +import kotlin.collections.List +import kotlin.collections.Map +import kotlin.collections.MutableMap + +data class Inventory( + val warehouseId: kotlin.String, + val productIds: List, + val stockCounts: Map, + val recentOrders: List +) { + fun getSerializedSize(): Int { + var size: Int = 0 + size = size + CodedOutputStream.computeStringSize(1, this.warehouseId) + for (elem: kotlin.String in this.productIds) { + size = size + CodedOutputStream.computeStringSize(2, elem) + } + for ((k, v) in this.stockCounts) { + size = size + CodedOutputStream.computeTagSize(3) + CodedOutputStream.computeUInt32SizeNoTag(CodedOutputStream.computeStringSize(1, k) + CodedOutputStream.computeInt32Size(2, v)) + CodedOutputStream.computeStringSize(1, k) + CodedOutputStream.computeInt32Size(2, v); + } + for (elem: Order in this.recentOrders) { + size = size + CodedOutputStream.computeTagSize(4) + CodedOutputStream.computeUInt32SizeNoTag(elem.getSerializedSize()) + elem.getSerializedSize() + } + return size + } + + @Throws(IOException::class) + fun writeTo(output: CodedOutputStream) { + output.writeString(1, this.warehouseId) + for (elem: kotlin.String in this.productIds) { + output.writeString(2, elem) + } + for ((k, v) in this.stockCounts) { + output.writeTag(3, 2); + output.writeUInt32NoTag(CodedOutputStream.computeStringSize(1, k) + CodedOutputStream.computeInt32Size(2, v)); + output.writeString(1, k); + output.writeInt32(2, v); + } + for (elem: Order in this.recentOrders) { + output.writeTag(4, 2) + output.writeUInt32NoTag(elem.getSerializedSize()) + elem.writeTo(output) + } + } + + companion object { + val MARSHALLER: Marshaller = + object : Marshaller { + override fun stream(value: Inventory): InputStream { + val bytes = ByteArray(value.getSerializedSize()) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch (e: IOException) { + throw RuntimeException(e) + } + return ByteArrayInputStream(bytes) + } + override fun parse(stream: InputStream): Inventory { + try { + return Inventory.parseFrom(CodedInputStream.newInstance(stream)) + } catch (e: IOException) { + throw RuntimeException(e) + } + } + } + + @Throws(IOException::class) + fun parseFrom(input: CodedInputStream): Inventory { + var warehouseId: kotlin.String = "" + var productIds: ArrayList = ArrayList() + var stockCounts: MutableMap = mutableMapOf() + var recentOrders: ArrayList = ArrayList() + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { warehouseId = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 2) { productIds.add(input.readString()) } + else if (WireFormat.getTagFieldNumber(tag) == 3) { val length = input.readRawVarint32(); + val oldLimit = input.pushLimit(length); + var mapKey = ""; + var mapValue = 0; + while (!input.isAtEnd()) { + val entryTag = input.readTag() + if (WireFormat.getTagFieldNumber(entryTag) == 1) { mapKey = input.readString() } + else if (WireFormat.getTagFieldNumber(entryTag) == 2) { mapValue = input.readInt32() } + else { input.skipField(entryTag) } + }; + input.popLimit(oldLimit); + stockCounts[mapKey] = mapValue; } + else if (WireFormat.getTagFieldNumber(tag) == 4) { val _length = input.readRawVarint32(); + val _oldLimit = input.pushLimit(_length); + recentOrders.add(Order.parseFrom(input)); + input.popLimit(_oldLimit); } + else { input.skipField(tag) } + } + return Inventory(warehouseId, productIds, stockCounts.toMap(), recentOrders) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/ListOrdersRequest.kt b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/ListOrdersRequest.kt new file mode 100644 index 0000000000..4be1f14759 --- /dev/null +++ b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/ListOrdersRequest.kt @@ -0,0 +1,65 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +data class ListOrdersRequest( + val customerId: kotlin.String, + val pageSize: Int +) { + fun getSerializedSize(): Int { + var size: Int = 0 + size = size + CodedOutputStream.computeStringSize(1, this.customerId) + size = size + CodedOutputStream.computeInt32Size(2, this.pageSize) + return size + } + + @Throws(IOException::class) + fun writeTo(output: CodedOutputStream) { + output.writeString(1, this.customerId) + output.writeInt32(2, this.pageSize) + } + + companion object { + val MARSHALLER: Marshaller = + object : Marshaller { + override fun stream(value: ListOrdersRequest): InputStream { + val bytes = ByteArray(value.getSerializedSize()) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch (e: IOException) { + throw RuntimeException(e) + } + return ByteArrayInputStream(bytes) + } + override fun parse(stream: InputStream): ListOrdersRequest { + try { + return ListOrdersRequest.parseFrom(CodedInputStream.newInstance(stream)) + } catch (e: IOException) { + throw RuntimeException(e) + } + } + } + + @Throws(IOException::class) + fun parseFrom(input: CodedInputStream): ListOrdersRequest { + var customerId: kotlin.String = "" + var pageSize: Int = 0 + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { customerId = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 2) { pageSize = input.readInt32() } + else { input.skipField(tag) } + } + return ListOrdersRequest(customerId, pageSize) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/Notification.kt b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/Notification.kt new file mode 100644 index 0000000000..5a1836190f --- /dev/null +++ b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/Notification.kt @@ -0,0 +1,85 @@ +package com.example.grpc + +import com.example.grpc.NotificationTarget.Email +import com.example.grpc.NotificationTarget.Phone +import com.example.grpc.NotificationTarget.WebhookUrl +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +data class Notification( + val message: kotlin.String, + val priority: Priority, + val target: NotificationTarget? +) { + fun getSerializedSize(): Int { + var size: Int = 0 + size = size + CodedOutputStream.computeStringSize(1, this.message) + size = size + CodedOutputStream.computeEnumSize(2, this.priority.toValue()) + when (val __r = this.target) { + null -> {} + is Email -> { val c = __r; size = size + CodedOutputStream.computeStringSize(3, c.email) } + is Phone -> { val c = __r; size = size + CodedOutputStream.computeStringSize(4, c.phone) } + is WebhookUrl -> { val c = __r; size = size + CodedOutputStream.computeStringSize(5, c.webhookUrl) } + } + return size + } + + @Throws(IOException::class) + fun writeTo(output: CodedOutputStream) { + output.writeString(1, this.message) + output.writeEnum(2, this.priority.toValue()) + when (val __r = this.target) { + null -> {} + is Email -> { val c = __r; output.writeString(3, c.email) } + is Phone -> { val c = __r; output.writeString(4, c.phone) } + is WebhookUrl -> { val c = __r; output.writeString(5, c.webhookUrl) } + } + } + + companion object { + val MARSHALLER: Marshaller = + object : Marshaller { + override fun stream(value: Notification): InputStream { + val bytes = ByteArray(value.getSerializedSize()) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch (e: IOException) { + throw RuntimeException(e) + } + return ByteArrayInputStream(bytes) + } + override fun parse(stream: InputStream): Notification { + try { + return Notification.parseFrom(CodedInputStream.newInstance(stream)) + } catch (e: IOException) { + throw RuntimeException(e) + } + } + } + + @Throws(IOException::class) + fun parseFrom(input: CodedInputStream): Notification { + var message: kotlin.String = "" + var priority: Priority = Priority.fromValue(0) + var target: NotificationTarget? = null + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { message = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 2) { priority = Priority.fromValue(input.readEnum()) } + else if (WireFormat.getTagFieldNumber(tag) == 3) { target = Email(input.readString()) } + else if (WireFormat.getTagFieldNumber(tag) == 4) { target = Phone(input.readString()) } + else if (WireFormat.getTagFieldNumber(tag) == 5) { target = WebhookUrl(input.readString()) } + else { input.skipField(tag) } + } + return Notification(message, priority, target) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/NotificationTarget.kt b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/NotificationTarget.kt new file mode 100644 index 0000000000..5e9aa20f37 --- /dev/null +++ b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/NotificationTarget.kt @@ -0,0 +1,12 @@ +package com.example.grpc + + + +/** OneOf type for target */ +sealed interface NotificationTarget { + data class Email(val email: kotlin.String) : NotificationTarget + + data class Phone(val phone: kotlin.String) : NotificationTarget + + data class WebhookUrl(val webhookUrl: kotlin.String) : NotificationTarget +} \ No newline at end of file diff --git a/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/OptionalFields.kt b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/OptionalFields.kt new file mode 100644 index 0000000000..1885ecb5e5 --- /dev/null +++ b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/OptionalFields.kt @@ -0,0 +1,93 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +data class OptionalFields( + val name: kotlin.String?, + val age: Int?, + val customer: Customer? +) { + fun getSerializedSize(): Int { + var size: Int = 0 + if (this.name != null) { + val v = this.name!!; + size = size + CodedOutputStream.computeStringSize(1, v); + } + if (this.age != null) { + val v = this.age!!; + size = size + CodedOutputStream.computeInt32Size(2, v); + } + if (this.customer != null) { + val v = this.customer!!; + size = size + CodedOutputStream.computeTagSize(3) + CodedOutputStream.computeUInt32SizeNoTag(v.getSerializedSize()) + v.getSerializedSize(); + } + return size + } + + @Throws(IOException::class) + fun writeTo(output: CodedOutputStream) { + if (this.name != null) { + val v = this.name!!; + output.writeString(1, v); + } + if (this.age != null) { + val v = this.age!!; + output.writeInt32(2, v); + } + if (this.customer != null) { + val v = this.customer!!; + output.writeTag(3, 2); + output.writeUInt32NoTag(v.getSerializedSize()); + v.writeTo(output); + } + } + + companion object { + val MARSHALLER: Marshaller = + object : Marshaller { + override fun stream(value: OptionalFields): InputStream { + val bytes = ByteArray(value.getSerializedSize()) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch (e: IOException) { + throw RuntimeException(e) + } + return ByteArrayInputStream(bytes) + } + override fun parse(stream: InputStream): OptionalFields { + try { + return OptionalFields.parseFrom(CodedInputStream.newInstance(stream)) + } catch (e: IOException) { + throw RuntimeException(e) + } + } + } + + @Throws(IOException::class) + fun parseFrom(input: CodedInputStream): OptionalFields { + var name: kotlin.String? = null + var age: Int? = null + var customer: Customer? = null + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { name = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 2) { age = input.readInt32() } + else if (WireFormat.getTagFieldNumber(tag) == 3) { val _length = input.readRawVarint32(); + val _oldLimit = input.pushLimit(_length); + customer = Customer.parseFrom(input); + input.popLimit(_oldLimit); } + else { input.skipField(tag) } + } + return OptionalFields(name, age, customer) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/Order.kt b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/Order.kt new file mode 100644 index 0000000000..8c3600fc2b --- /dev/null +++ b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/Order.kt @@ -0,0 +1,90 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException +import java.time.Instant + +data class Order( + val orderId: OrderId, + val customerId: CustomerId, + val amountCents: kotlin.Long, + val createdAt: Instant +) { + fun getSerializedSize(): Int { + var size: Int = 0 + size = size + CodedOutputStream.computeStringSize(1, this.orderId.unwrap()) + size = size + CodedOutputStream.computeStringSize(2, this.customerId.unwrap()) + size = size + CodedOutputStream.computeInt64Size(3, this.amountCents) + size = size + CodedOutputStream.computeTagSize(4) + CodedOutputStream.computeUInt32SizeNoTag(CodedOutputStream.computeInt64Size(1, this.createdAt.getEpochSecond()) + CodedOutputStream.computeInt32Size(2, this.createdAt.getNano())) + CodedOutputStream.computeInt64Size(1, this.createdAt.getEpochSecond()) + CodedOutputStream.computeInt32Size(2, this.createdAt.getNano()) + return size + } + + @Throws(IOException::class) + fun writeTo(output: CodedOutputStream) { + output.writeString(1, this.orderId.unwrap()) + output.writeString(2, this.customerId.unwrap()) + output.writeInt64(3, this.amountCents) + output.writeTag(4, 2) + output.writeUInt32NoTag(CodedOutputStream.computeInt64Size(1, this.createdAt.getEpochSecond()) + CodedOutputStream.computeInt32Size(2, this.createdAt.getNano())) + output.writeInt64(1, this.createdAt.getEpochSecond()) + output.writeInt32(2, this.createdAt.getNano()) + } + + companion object { + val MARSHALLER: Marshaller = + object : Marshaller { + override fun stream(value: Order): InputStream { + val bytes = ByteArray(value.getSerializedSize()) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch (e: IOException) { + throw RuntimeException(e) + } + return ByteArrayInputStream(bytes) + } + override fun parse(stream: InputStream): Order { + try { + return Order.parseFrom(CodedInputStream.newInstance(stream)) + } catch (e: IOException) { + throw RuntimeException(e) + } + } + } + + @Throws(IOException::class) + fun parseFrom(input: CodedInputStream): Order { + var orderId: OrderId = OrderId.valueOf("") + var customerId: CustomerId = CustomerId.valueOf("") + var amountCents: kotlin.Long = 0L + var createdAt: Instant = Instant.EPOCH + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { orderId = OrderId.valueOf(input.readString()) } + else if (WireFormat.getTagFieldNumber(tag) == 2) { customerId = CustomerId.valueOf(input.readString()) } + else if (WireFormat.getTagFieldNumber(tag) == 3) { amountCents = input.readInt64() } + else if (WireFormat.getTagFieldNumber(tag) == 4) { val _length = input.readRawVarint32(); + val _oldLimit = input.pushLimit(_length); + var _tsSeconds = 0L; + var _tsNanos = 0; + while (!input.isAtEnd()) { + val _tsTag = input.readTag() + if (WireFormat.getTagFieldNumber(_tsTag) == 1) { _tsSeconds = input.readInt64() } + else if (WireFormat.getTagFieldNumber(_tsTag) == 2) { _tsNanos = input.readInt32() } + else { input.skipField(_tsTag) } + }; + createdAt = Instant.ofEpochSecond(_tsSeconds, _tsNanos.toLong()); + input.popLimit(_oldLimit); } + else { input.skipField(tag) } + } + return Order(orderId, customerId, amountCents, createdAt) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/OrderId.kt b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/OrderId.kt new file mode 100644 index 0000000000..a8168148d4 --- /dev/null +++ b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/OrderId.kt @@ -0,0 +1,22 @@ +package com.example.grpc + + + +/** Wrapper type for typr.grpc.GrpcCodegen$$$Lambda/0x00007fc001118400@45385f75 */ +data class OrderId(val value: kotlin.String) { + /** Get the underlying value */ + fun unwrap(): kotlin.String { + return this.value + } + + override fun toString(): kotlin.String { + return value + } + + companion object { + /** Create a OrderId from a raw value */ + fun valueOf(v: kotlin.String): OrderId { + return OrderId(v) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/OrderService.kt b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/OrderService.kt new file mode 100644 index 0000000000..6be15dd6b3 --- /dev/null +++ b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/OrderService.kt @@ -0,0 +1,17 @@ +package com.example.grpc + +import io.smallrye.mutiny.Uni +import kotlin.collections.Iterator + +/** Clean service interface for OrderService gRPC service */ +interface OrderService { + abstract fun chat(requests: Iterator): Iterator + + abstract fun createOrder(request: CreateOrderRequest): Uni + + abstract fun getCustomer(request: GetCustomerRequest): Uni + + abstract fun listOrders(request: ListOrdersRequest): Iterator + + abstract fun submitOrders(requests: Iterator): Uni +} \ No newline at end of file diff --git a/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/OrderServiceClient.kt b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/OrderServiceClient.kt new file mode 100644 index 0000000000..24080680da --- /dev/null +++ b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/OrderServiceClient.kt @@ -0,0 +1,46 @@ +package com.example.grpc + +import io.grpc.CallOptions +import io.grpc.Channel +import io.grpc.MethodDescriptor +import io.grpc.MethodDescriptor.MethodType +import io.grpc.stub.ClientCalls +import io.quarkus.grpc.GrpcClient +import io.smallrye.mutiny.Uni +import java.lang.UnsupportedOperationException +import kotlin.collections.Iterator + +/** gRPC client wrapper for OrderService - wraps Channel with clean types */ +data class OrderServiceClient(@field:GrpcClient("OrderService") val channel: Channel) : OrderService { + override fun chat(requests: Iterator): Iterator { + throw UnsupportedOperationException("Bidi streaming not yet implemented in client wrapper") + } + + override fun createOrder(request: CreateOrderRequest): Uni { + return Uni.createFrom().item({ ClientCalls.blockingUnaryCall(channel, OrderServiceClient.CREATE_ORDER, CallOptions.DEFAULT, request) }) + } + + override fun getCustomer(request: GetCustomerRequest): Uni { + return Uni.createFrom().item({ ClientCalls.blockingUnaryCall(channel, OrderServiceClient.GET_CUSTOMER, CallOptions.DEFAULT, request) }) + } + + override fun listOrders(request: ListOrdersRequest): Iterator { + return ClientCalls.blockingServerStreamingCall(channel, OrderServiceClient.LIST_ORDERS, CallOptions.DEFAULT, request) + } + + override fun submitOrders(requests: Iterator): Uni { + throw UnsupportedOperationException("Client streaming not yet implemented in client wrapper") + } + + companion object { + val CHAT: MethodDescriptor = MethodDescriptor.newBuilder(ChatMessage.MARSHALLER, ChatMessage.MARSHALLER).setType(MethodType.BIDI_STREAMING).setFullMethodName("testgrpc.OrderService/Chat").build() + + val CREATE_ORDER: MethodDescriptor = MethodDescriptor.newBuilder(CreateOrderRequest.MARSHALLER, CreateOrderResponse.MARSHALLER).setType(MethodType.UNARY).setFullMethodName("testgrpc.OrderService/CreateOrder").build() + + val GET_CUSTOMER: MethodDescriptor = MethodDescriptor.newBuilder(GetCustomerRequest.MARSHALLER, GetCustomerResponse.MARSHALLER).setType(MethodType.UNARY).setFullMethodName("testgrpc.OrderService/GetCustomer").build() + + val LIST_ORDERS: MethodDescriptor = MethodDescriptor.newBuilder(ListOrdersRequest.MARSHALLER, OrderUpdate.MARSHALLER).setType(MethodType.SERVER_STREAMING).setFullMethodName("testgrpc.OrderService/ListOrders").build() + + val SUBMIT_ORDERS: MethodDescriptor = MethodDescriptor.newBuilder(CreateOrderRequest.MARSHALLER, OrderSummary.MARSHALLER).setType(MethodType.CLIENT_STREAMING).setFullMethodName("testgrpc.OrderService/SubmitOrders").build() + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/OrderServiceServer.kt b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/OrderServiceServer.kt new file mode 100644 index 0000000000..0bd794cbca --- /dev/null +++ b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/OrderServiceServer.kt @@ -0,0 +1,35 @@ +package com.example.grpc + +import io.grpc.BindableService +import io.grpc.MethodDescriptor +import io.grpc.MethodDescriptor.MethodType +import io.grpc.ServerServiceDefinition +import io.grpc.stub.ServerCalls +import io.quarkus.grpc.GrpcService +import jakarta.inject.Singleton +import java.lang.UnsupportedOperationException + +/** gRPC server adapter for OrderService - delegates to clean service interface */ +data class OrderServiceServer(val delegate: OrderService) : BindableService { + override fun bindService(): ServerServiceDefinition { + return ServerServiceDefinition.builder("testgrpc.OrderService").addMethod(OrderServiceServer.GET_CUSTOMER, ServerCalls.asyncUnaryCall({ request, responseObserver -> delegate.getCustomer(request).subscribe().with({ response -> responseObserver.onNext(response) + responseObserver.onCompleted() }, { error -> responseObserver.onError(error) }) })).addMethod(OrderServiceServer.CREATE_ORDER, ServerCalls.asyncUnaryCall({ request, responseObserver -> delegate.createOrder(request).subscribe().with({ response -> responseObserver.onNext(response) + responseObserver.onCompleted() }, { error -> responseObserver.onError(error) }) })).addMethod(OrderServiceServer.LIST_ORDERS, ServerCalls.asyncServerStreamingCall({ request, responseObserver -> val results = delegate.listOrders(request) + while (results.hasNext()) { + responseObserver.onNext(results.next()) + } + responseObserver.onCompleted() })).addMethod(OrderServiceServer.SUBMIT_ORDERS, ServerCalls.asyncClientStreamingCall({ responseObserver -> throw UnsupportedOperationException("Client streaming not yet implemented in server adapter") })).addMethod(OrderServiceServer.CHAT, ServerCalls.asyncBidiStreamingCall({ responseObserver -> throw UnsupportedOperationException("Bidi streaming not yet implemented in server adapter") })).build() + } + + companion object { + val CHAT: MethodDescriptor = MethodDescriptor.newBuilder(ChatMessage.MARSHALLER, ChatMessage.MARSHALLER).setType(MethodType.BIDI_STREAMING).setFullMethodName("testgrpc.OrderService/Chat").build() + + val CREATE_ORDER: MethodDescriptor = MethodDescriptor.newBuilder(CreateOrderRequest.MARSHALLER, CreateOrderResponse.MARSHALLER).setType(MethodType.UNARY).setFullMethodName("testgrpc.OrderService/CreateOrder").build() + + val GET_CUSTOMER: MethodDescriptor = MethodDescriptor.newBuilder(GetCustomerRequest.MARSHALLER, GetCustomerResponse.MARSHALLER).setType(MethodType.UNARY).setFullMethodName("testgrpc.OrderService/GetCustomer").build() + + val LIST_ORDERS: MethodDescriptor = MethodDescriptor.newBuilder(ListOrdersRequest.MARSHALLER, OrderUpdate.MARSHALLER).setType(MethodType.SERVER_STREAMING).setFullMethodName("testgrpc.OrderService/ListOrders").build() + + val SUBMIT_ORDERS: MethodDescriptor = MethodDescriptor.newBuilder(CreateOrderRequest.MARSHALLER, OrderSummary.MARSHALLER).setType(MethodType.CLIENT_STREAMING).setFullMethodName("testgrpc.OrderService/SubmitOrders").build() + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/OrderStatus.kt b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/OrderStatus.kt new file mode 100644 index 0000000000..5c0bc5f498 --- /dev/null +++ b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/OrderStatus.kt @@ -0,0 +1,39 @@ +package com.example.grpc + +import java.lang.IllegalArgumentException + +enum class OrderStatus(val value: kotlin.String) { + ORDER_STATUS_UNSPECIFIED("ORDER_STATUS_UNSPECIFIED"), + ORDER_STATUS_PENDING("ORDER_STATUS_PENDING"), + ORDER_STATUS_PROCESSING("ORDER_STATUS_PROCESSING"), + ORDER_STATUS_SHIPPED("ORDER_STATUS_SHIPPED"), + ORDER_STATUS_DELIVERED("ORDER_STATUS_DELIVERED"), + ORDER_STATUS_CANCELLED("ORDER_STATUS_CANCELLED"); + + fun toValue(): Int { + if (this.toString().equals("ORDER_STATUS_UNSPECIFIED")) { return 0 } + else if (this.toString().equals("ORDER_STATUS_PENDING")) { return 1 } + else if (this.toString().equals("ORDER_STATUS_PROCESSING")) { return 2 } + else if (this.toString().equals("ORDER_STATUS_SHIPPED")) { return 3 } + else if (this.toString().equals("ORDER_STATUS_DELIVERED")) { return 4 } + else if (this.toString().equals("ORDER_STATUS_CANCELLED")) { return 5 } + else { return 0 } + } + + companion object { + val Names: kotlin.String = entries.joinToString(", ") { it.value } + val ByName: kotlin.collections.Map = entries.associateBy { it.value } + fun fromValue(value: Int): OrderStatus { + if (value == 0) { return OrderStatus.ORDER_STATUS_UNSPECIFIED } + else if (value == 1) { return OrderStatus.ORDER_STATUS_PENDING } + else if (value == 2) { return OrderStatus.ORDER_STATUS_PROCESSING } + else if (value == 3) { return OrderStatus.ORDER_STATUS_SHIPPED } + else if (value == 4) { return OrderStatus.ORDER_STATUS_DELIVERED } + else if (value == 5) { return OrderStatus.ORDER_STATUS_CANCELLED } + else { throw IllegalArgumentException("Unknown enum value: " + value) } + } + + fun force(str: kotlin.String): OrderStatus = + ByName[str] ?: throw RuntimeException("'$str' does not match any of the following legal values: $Names") + } +} diff --git a/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/OrderSummary.kt b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/OrderSummary.kt new file mode 100644 index 0000000000..34645e077f --- /dev/null +++ b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/OrderSummary.kt @@ -0,0 +1,65 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +data class OrderSummary( + val totalOrders: Int, + val totalAmountCents: kotlin.Long +) { + fun getSerializedSize(): Int { + var size: Int = 0 + size = size + CodedOutputStream.computeInt32Size(1, this.totalOrders) + size = size + CodedOutputStream.computeInt64Size(2, this.totalAmountCents) + return size + } + + @Throws(IOException::class) + fun writeTo(output: CodedOutputStream) { + output.writeInt32(1, this.totalOrders) + output.writeInt64(2, this.totalAmountCents) + } + + companion object { + val MARSHALLER: Marshaller = + object : Marshaller { + override fun stream(value: OrderSummary): InputStream { + val bytes = ByteArray(value.getSerializedSize()) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch (e: IOException) { + throw RuntimeException(e) + } + return ByteArrayInputStream(bytes) + } + override fun parse(stream: InputStream): OrderSummary { + try { + return OrderSummary.parseFrom(CodedInputStream.newInstance(stream)) + } catch (e: IOException) { + throw RuntimeException(e) + } + } + } + + @Throws(IOException::class) + fun parseFrom(input: CodedInputStream): OrderSummary { + var totalOrders: Int = 0 + var totalAmountCents: kotlin.Long = 0L + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { totalOrders = input.readInt32() } + else if (WireFormat.getTagFieldNumber(tag) == 2) { totalAmountCents = input.readInt64() } + else { input.skipField(tag) } + } + return OrderSummary(totalOrders, totalAmountCents) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/OrderUpdate.kt b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/OrderUpdate.kt new file mode 100644 index 0000000000..30097bf1f2 --- /dev/null +++ b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/OrderUpdate.kt @@ -0,0 +1,85 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException +import java.time.Instant + +data class OrderUpdate( + val orderId: kotlin.String, + val status: OrderStatus, + val updatedAt: Instant +) { + fun getSerializedSize(): Int { + var size: Int = 0 + size = size + CodedOutputStream.computeStringSize(1, this.orderId) + size = size + CodedOutputStream.computeEnumSize(2, this.status.toValue()) + size = size + CodedOutputStream.computeTagSize(3) + CodedOutputStream.computeUInt32SizeNoTag(CodedOutputStream.computeInt64Size(1, this.updatedAt.getEpochSecond()) + CodedOutputStream.computeInt32Size(2, this.updatedAt.getNano())) + CodedOutputStream.computeInt64Size(1, this.updatedAt.getEpochSecond()) + CodedOutputStream.computeInt32Size(2, this.updatedAt.getNano()) + return size + } + + @Throws(IOException::class) + fun writeTo(output: CodedOutputStream) { + output.writeString(1, this.orderId) + output.writeEnum(2, this.status.toValue()) + output.writeTag(3, 2) + output.writeUInt32NoTag(CodedOutputStream.computeInt64Size(1, this.updatedAt.getEpochSecond()) + CodedOutputStream.computeInt32Size(2, this.updatedAt.getNano())) + output.writeInt64(1, this.updatedAt.getEpochSecond()) + output.writeInt32(2, this.updatedAt.getNano()) + } + + companion object { + val MARSHALLER: Marshaller = + object : Marshaller { + override fun stream(value: OrderUpdate): InputStream { + val bytes = ByteArray(value.getSerializedSize()) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch (e: IOException) { + throw RuntimeException(e) + } + return ByteArrayInputStream(bytes) + } + override fun parse(stream: InputStream): OrderUpdate { + try { + return OrderUpdate.parseFrom(CodedInputStream.newInstance(stream)) + } catch (e: IOException) { + throw RuntimeException(e) + } + } + } + + @Throws(IOException::class) + fun parseFrom(input: CodedInputStream): OrderUpdate { + var orderId: kotlin.String = "" + var status: OrderStatus = OrderStatus.fromValue(0) + var updatedAt: Instant = Instant.EPOCH + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { orderId = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 2) { status = OrderStatus.fromValue(input.readEnum()) } + else if (WireFormat.getTagFieldNumber(tag) == 3) { val _length = input.readRawVarint32(); + val _oldLimit = input.pushLimit(_length); + var _tsSeconds = 0L; + var _tsNanos = 0; + while (!input.isAtEnd()) { + val _tsTag = input.readTag() + if (WireFormat.getTagFieldNumber(_tsTag) == 1) { _tsSeconds = input.readInt64() } + else if (WireFormat.getTagFieldNumber(_tsTag) == 2) { _tsNanos = input.readInt32() } + else { input.skipField(_tsTag) } + }; + updatedAt = Instant.ofEpochSecond(_tsSeconds, _tsNanos.toLong()); + input.popLimit(_oldLimit); } + else { input.skipField(tag) } + } + return OrderUpdate(orderId, status, updatedAt) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/Outer.kt b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/Outer.kt new file mode 100644 index 0000000000..79e2c7fbba --- /dev/null +++ b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/Outer.kt @@ -0,0 +1,74 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +data class Outer( + val name: kotlin.String, + val inner: Inner? +) { + fun getSerializedSize(): Int { + var size: Int = 0 + size = size + CodedOutputStream.computeStringSize(1, this.name) + if ((this.inner != null)) { + size = size + CodedOutputStream.computeTagSize(2) + CodedOutputStream.computeUInt32SizeNoTag(this.inner.getSerializedSize()) + this.inner.getSerializedSize() + } + return size + } + + @Throws(IOException::class) + fun writeTo(output: CodedOutputStream) { + output.writeString(1, this.name) + if ((this.inner != null)) { + output.writeTag(2, 2); + output.writeUInt32NoTag(this.inner.getSerializedSize()); + this.inner.writeTo(output); + } + } + + companion object { + val MARSHALLER: Marshaller = + object : Marshaller { + override fun stream(value: Outer): InputStream { + val bytes = ByteArray(value.getSerializedSize()) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch (e: IOException) { + throw RuntimeException(e) + } + return ByteArrayInputStream(bytes) + } + override fun parse(stream: InputStream): Outer { + try { + return Outer.parseFrom(CodedInputStream.newInstance(stream)) + } catch (e: IOException) { + throw RuntimeException(e) + } + } + } + + @Throws(IOException::class) + fun parseFrom(input: CodedInputStream): Outer { + var name: kotlin.String = "" + var inner: Inner? = null + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { name = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 2) { val _length = input.readRawVarint32(); + val _oldLimit = input.pushLimit(_length); + inner = Inner.parseFrom(input); + input.popLimit(_oldLimit); } + else { input.skipField(tag) } + } + return Outer(name, inner) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/PaymentMethod.kt b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/PaymentMethod.kt new file mode 100644 index 0000000000..691ab3a966 --- /dev/null +++ b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/PaymentMethod.kt @@ -0,0 +1,104 @@ +package com.example.grpc + +import com.example.grpc.PaymentMethodMethod.BankTransferValue +import com.example.grpc.PaymentMethodMethod.CreditCardValue +import com.example.grpc.PaymentMethodMethod.WalletValue +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +data class PaymentMethod( + val id: kotlin.String, + val method: PaymentMethodMethod? +) { + fun getSerializedSize(): Int { + var size: Int = 0 + size = size + CodedOutputStream.computeStringSize(1, this.id) + when (val __r = this.method) { + null -> {} + is CreditCardValue -> { val c = __r; size = size + CodedOutputStream.computeTagSize(2) + CodedOutputStream.computeUInt32SizeNoTag(c.creditCard.getSerializedSize()) + c.creditCard.getSerializedSize() } + is BankTransferValue -> { val c = __r; size = size + CodedOutputStream.computeTagSize(3) + CodedOutputStream.computeUInt32SizeNoTag(c.bankTransfer.getSerializedSize()) + c.bankTransfer.getSerializedSize() } + is WalletValue -> { val c = __r; size = size + CodedOutputStream.computeTagSize(4) + CodedOutputStream.computeUInt32SizeNoTag(c.wallet.getSerializedSize()) + c.wallet.getSerializedSize() } + } + return size + } + + @Throws(IOException::class) + fun writeTo(output: CodedOutputStream) { + output.writeString(1, this.id) + when (val __r = this.method) { + null -> {} + is CreditCardValue -> { + val c = __r + output.writeTag(2, 2); + output.writeUInt32NoTag(c.creditCard.getSerializedSize()); + c.creditCard.writeTo(output); + } + is BankTransferValue -> { + val c = __r + output.writeTag(3, 2); + output.writeUInt32NoTag(c.bankTransfer.getSerializedSize()); + c.bankTransfer.writeTo(output); + } + is WalletValue -> { + val c = __r + output.writeTag(4, 2); + output.writeUInt32NoTag(c.wallet.getSerializedSize()); + c.wallet.writeTo(output); + } + } + } + + companion object { + val MARSHALLER: Marshaller = + object : Marshaller { + override fun stream(value: PaymentMethod): InputStream { + val bytes = ByteArray(value.getSerializedSize()) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch (e: IOException) { + throw RuntimeException(e) + } + return ByteArrayInputStream(bytes) + } + override fun parse(stream: InputStream): PaymentMethod { + try { + return PaymentMethod.parseFrom(CodedInputStream.newInstance(stream)) + } catch (e: IOException) { + throw RuntimeException(e) + } + } + } + + @Throws(IOException::class) + fun parseFrom(input: CodedInputStream): PaymentMethod { + var id: kotlin.String = "" + var method: PaymentMethodMethod? = null + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { id = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 2) { val _length = input.readRawVarint32(); + val _oldLimit = input.pushLimit(_length); + method = CreditCardValue(CreditCard.parseFrom(input)); + input.popLimit(_oldLimit); } + else if (WireFormat.getTagFieldNumber(tag) == 3) { val _length = input.readRawVarint32(); + val _oldLimit = input.pushLimit(_length); + method = BankTransferValue(BankTransfer.parseFrom(input)); + input.popLimit(_oldLimit); } + else if (WireFormat.getTagFieldNumber(tag) == 4) { val _length = input.readRawVarint32(); + val _oldLimit = input.pushLimit(_length); + method = WalletValue(Wallet.parseFrom(input)); + input.popLimit(_oldLimit); } + else { input.skipField(tag) } + } + return PaymentMethod(id, method) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/PaymentMethodMethod.kt b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/PaymentMethodMethod.kt new file mode 100644 index 0000000000..9850a8acd3 --- /dev/null +++ b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/PaymentMethodMethod.kt @@ -0,0 +1,12 @@ +package com.example.grpc + + + +/** OneOf type for method */ +sealed interface PaymentMethodMethod { + data class BankTransferValue(val bankTransfer: BankTransfer) : PaymentMethodMethod + + data class CreditCardValue(val creditCard: CreditCard) : PaymentMethodMethod + + data class WalletValue(val wallet: Wallet) : PaymentMethodMethod +} \ No newline at end of file diff --git a/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/Priority.kt b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/Priority.kt new file mode 100644 index 0000000000..d800dfda37 --- /dev/null +++ b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/Priority.kt @@ -0,0 +1,36 @@ +package com.example.grpc + +import java.lang.IllegalArgumentException + +enum class Priority(val value: kotlin.String) { + PRIORITY_UNSPECIFIED("PRIORITY_UNSPECIFIED"), + PRIORITY_LOW("PRIORITY_LOW"), + PRIORITY_MEDIUM("PRIORITY_MEDIUM"), + PRIORITY_HIGH("PRIORITY_HIGH"), + PRIORITY_CRITICAL("PRIORITY_CRITICAL"); + + fun toValue(): Int { + if (this.toString().equals("PRIORITY_UNSPECIFIED")) { return 0 } + else if (this.toString().equals("PRIORITY_LOW")) { return 1 } + else if (this.toString().equals("PRIORITY_MEDIUM")) { return 2 } + else if (this.toString().equals("PRIORITY_HIGH")) { return 3 } + else if (this.toString().equals("PRIORITY_CRITICAL")) { return 4 } + else { return 0 } + } + + companion object { + val Names: kotlin.String = entries.joinToString(", ") { it.value } + val ByName: kotlin.collections.Map = entries.associateBy { it.value } + fun fromValue(value: Int): Priority { + if (value == 0) { return Priority.PRIORITY_UNSPECIFIED } + else if (value == 1) { return Priority.PRIORITY_LOW } + else if (value == 2) { return Priority.PRIORITY_MEDIUM } + else if (value == 3) { return Priority.PRIORITY_HIGH } + else if (value == 4) { return Priority.PRIORITY_CRITICAL } + else { throw IllegalArgumentException("Unknown enum value: " + value) } + } + + fun force(str: kotlin.String): Priority = + ByName[str] ?: throw RuntimeException("'$str' does not match any of the following legal values: $Names") + } +} diff --git a/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/ScalarTypes.kt b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/ScalarTypes.kt new file mode 100644 index 0000000000..9b884d2baa --- /dev/null +++ b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/ScalarTypes.kt @@ -0,0 +1,131 @@ +package com.example.grpc + +import com.google.protobuf.ByteString +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +data class ScalarTypes( + val doubleVal: kotlin.Double, + val floatVal: kotlin.Float, + val int32Val: Int, + val int64Val: kotlin.Long, + val uint32Val: Int, + val uint64Val: kotlin.Long, + val sint32Val: Int, + val sint64Val: kotlin.Long, + val fixed32Val: Int, + val fixed64Val: kotlin.Long, + val sfixed32Val: Int, + val sfixed64Val: kotlin.Long, + val boolVal: kotlin.Boolean, + val stringVal: kotlin.String, + val bytesVal: ByteString +) { + fun getSerializedSize(): Int { + var size: Int = 0 + size = size + CodedOutputStream.computeDoubleSize(1, this.doubleVal) + size = size + CodedOutputStream.computeFloatSize(2, this.floatVal) + size = size + CodedOutputStream.computeInt32Size(3, this.int32Val) + size = size + CodedOutputStream.computeInt64Size(4, this.int64Val) + size = size + CodedOutputStream.computeUInt32Size(5, this.uint32Val) + size = size + CodedOutputStream.computeUInt64Size(6, this.uint64Val) + size = size + CodedOutputStream.computeSInt32Size(7, this.sint32Val) + size = size + CodedOutputStream.computeSInt64Size(8, this.sint64Val) + size = size + CodedOutputStream.computeFixed32Size(9, this.fixed32Val) + size = size + CodedOutputStream.computeFixed64Size(10, this.fixed64Val) + size = size + CodedOutputStream.computeSFixed32Size(11, this.sfixed32Val) + size = size + CodedOutputStream.computeSFixed64Size(12, this.sfixed64Val) + size = size + CodedOutputStream.computeBoolSize(13, this.boolVal) + size = size + CodedOutputStream.computeStringSize(14, this.stringVal) + size = size + CodedOutputStream.computeBytesSize(15, this.bytesVal) + return size + } + + @Throws(IOException::class) + fun writeTo(output: CodedOutputStream) { + output.writeDouble(1, this.doubleVal) + output.writeFloat(2, this.floatVal) + output.writeInt32(3, this.int32Val) + output.writeInt64(4, this.int64Val) + output.writeUInt32(5, this.uint32Val) + output.writeUInt64(6, this.uint64Val) + output.writeSInt32(7, this.sint32Val) + output.writeSInt64(8, this.sint64Val) + output.writeFixed32(9, this.fixed32Val) + output.writeFixed64(10, this.fixed64Val) + output.writeSFixed32(11, this.sfixed32Val) + output.writeSFixed64(12, this.sfixed64Val) + output.writeBool(13, this.boolVal) + output.writeString(14, this.stringVal) + output.writeBytes(15, this.bytesVal) + } + + companion object { + val MARSHALLER: Marshaller = + object : Marshaller { + override fun stream(value: ScalarTypes): InputStream { + val bytes = ByteArray(value.getSerializedSize()) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch (e: IOException) { + throw RuntimeException(e) + } + return ByteArrayInputStream(bytes) + } + override fun parse(stream: InputStream): ScalarTypes { + try { + return ScalarTypes.parseFrom(CodedInputStream.newInstance(stream)) + } catch (e: IOException) { + throw RuntimeException(e) + } + } + } + + @Throws(IOException::class) + fun parseFrom(input: CodedInputStream): ScalarTypes { + var doubleVal: kotlin.Double = 0.0 + var floatVal: kotlin.Float = 0.0f + var int32Val: Int = 0 + var int64Val: kotlin.Long = 0L + var uint32Val: Int = 0 + var uint64Val: kotlin.Long = 0L + var sint32Val: Int = 0 + var sint64Val: kotlin.Long = 0L + var fixed32Val: Int = 0 + var fixed64Val: kotlin.Long = 0L + var sfixed32Val: Int = 0 + var sfixed64Val: kotlin.Long = 0L + var boolVal: kotlin.Boolean = false + var stringVal: kotlin.String = "" + var bytesVal: ByteString = ByteString.EMPTY + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { doubleVal = input.readDouble() } + else if (WireFormat.getTagFieldNumber(tag) == 2) { floatVal = input.readFloat() } + else if (WireFormat.getTagFieldNumber(tag) == 3) { int32Val = input.readInt32() } + else if (WireFormat.getTagFieldNumber(tag) == 4) { int64Val = input.readInt64() } + else if (WireFormat.getTagFieldNumber(tag) == 5) { uint32Val = input.readUInt32() } + else if (WireFormat.getTagFieldNumber(tag) == 6) { uint64Val = input.readUInt64() } + else if (WireFormat.getTagFieldNumber(tag) == 7) { sint32Val = input.readSInt32() } + else if (WireFormat.getTagFieldNumber(tag) == 8) { sint64Val = input.readSInt64() } + else if (WireFormat.getTagFieldNumber(tag) == 9) { fixed32Val = input.readFixed32() } + else if (WireFormat.getTagFieldNumber(tag) == 10) { fixed64Val = input.readFixed64() } + else if (WireFormat.getTagFieldNumber(tag) == 11) { sfixed32Val = input.readSFixed32() } + else if (WireFormat.getTagFieldNumber(tag) == 12) { sfixed64Val = input.readSFixed64() } + else if (WireFormat.getTagFieldNumber(tag) == 13) { boolVal = input.readBool() } + else if (WireFormat.getTagFieldNumber(tag) == 14) { stringVal = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 15) { bytesVal = input.readBytes() } + else { input.skipField(tag) } + } + return ScalarTypes(doubleVal, floatVal, int32Val, int64Val, uint32Val, uint64Val, sint32Val, sint64Val, fixed32Val, fixed64Val, sfixed32Val, sfixed64Val, boolVal, stringVal, bytesVal) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/Wallet.kt b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/Wallet.kt new file mode 100644 index 0000000000..48942add65 --- /dev/null +++ b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/Wallet.kt @@ -0,0 +1,65 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +data class Wallet( + val walletId: kotlin.String, + val provider: kotlin.String +) { + fun getSerializedSize(): Int { + var size: Int = 0 + size = size + CodedOutputStream.computeStringSize(1, this.walletId) + size = size + CodedOutputStream.computeStringSize(2, this.provider) + return size + } + + @Throws(IOException::class) + fun writeTo(output: CodedOutputStream) { + output.writeString(1, this.walletId) + output.writeString(2, this.provider) + } + + companion object { + val MARSHALLER: Marshaller = + object : Marshaller { + override fun stream(value: Wallet): InputStream { + val bytes = ByteArray(value.getSerializedSize()) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch (e: IOException) { + throw RuntimeException(e) + } + return ByteArrayInputStream(bytes) + } + override fun parse(stream: InputStream): Wallet { + try { + return Wallet.parseFrom(CodedInputStream.newInstance(stream)) + } catch (e: IOException) { + throw RuntimeException(e) + } + } + } + + @Throws(IOException::class) + fun parseFrom(input: CodedInputStream): Wallet { + var walletId: kotlin.String = "" + var provider: kotlin.String = "" + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { walletId = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 2) { provider = input.readString() } + else { input.skipField(tag) } + } + return Wallet(walletId, provider) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/WellKnownTypesMessage.kt b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/WellKnownTypesMessage.kt new file mode 100644 index 0000000000..829678a1c0 --- /dev/null +++ b/testers/grpc/kotlin-quarkus/generated-and-checked-in/com/example/grpc/WellKnownTypesMessage.kt @@ -0,0 +1,146 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException +import java.time.Duration +import java.time.Instant + +data class WellKnownTypesMessage( + val createdAt: Instant, + val ttl: Duration, + val nullableString: kotlin.String?, + val nullableInt: Int?, + val nullableBool: kotlin.Boolean? +) { + fun getSerializedSize(): Int { + var size: Int = 0 + size = size + CodedOutputStream.computeTagSize(1) + CodedOutputStream.computeUInt32SizeNoTag(CodedOutputStream.computeInt64Size(1, this.createdAt.getEpochSecond()) + CodedOutputStream.computeInt32Size(2, this.createdAt.getNano())) + CodedOutputStream.computeInt64Size(1, this.createdAt.getEpochSecond()) + CodedOutputStream.computeInt32Size(2, this.createdAt.getNano()) + size = size + CodedOutputStream.computeTagSize(2) + CodedOutputStream.computeUInt32SizeNoTag(CodedOutputStream.computeInt64Size(1, this.ttl.getSeconds()) + CodedOutputStream.computeInt32Size(2, this.ttl.getNano())) + CodedOutputStream.computeInt64Size(1, this.ttl.getSeconds()) + CodedOutputStream.computeInt32Size(2, this.ttl.getNano()) + if (this.nullableString != null) { + val v = this.nullableString!!; + size = size + CodedOutputStream.computeTagSize(3) + CodedOutputStream.computeUInt32SizeNoTag(CodedOutputStream.computeStringSize(1, v)) + CodedOutputStream.computeStringSize(1, v); + } + if (this.nullableInt != null) { + val v = this.nullableInt!!; + size = size + CodedOutputStream.computeTagSize(4) + CodedOutputStream.computeUInt32SizeNoTag(CodedOutputStream.computeInt32Size(1, v)) + CodedOutputStream.computeInt32Size(1, v); + } + if (this.nullableBool != null) { + val v = this.nullableBool!!; + size = size + CodedOutputStream.computeTagSize(5) + CodedOutputStream.computeUInt32SizeNoTag(CodedOutputStream.computeBoolSize(1, v)) + CodedOutputStream.computeBoolSize(1, v); + } + return size + } + + @Throws(IOException::class) + fun writeTo(output: CodedOutputStream) { + output.writeTag(1, 2) + output.writeUInt32NoTag(CodedOutputStream.computeInt64Size(1, this.createdAt.getEpochSecond()) + CodedOutputStream.computeInt32Size(2, this.createdAt.getNano())) + output.writeInt64(1, this.createdAt.getEpochSecond()) + output.writeInt32(2, this.createdAt.getNano()) + output.writeTag(2, 2) + output.writeUInt32NoTag(CodedOutputStream.computeInt64Size(1, this.ttl.getSeconds()) + CodedOutputStream.computeInt32Size(2, this.ttl.getNano())) + output.writeInt64(1, this.ttl.getSeconds()) + output.writeInt32(2, this.ttl.getNano()) + if (this.nullableString != null) { + val v = this.nullableString!!; + output.writeTag(3, 2); + output.writeUInt32NoTag(CodedOutputStream.computeStringSize(1, v)); + output.writeString(1, v); + } + if (this.nullableInt != null) { + val v = this.nullableInt!!; + output.writeTag(4, 2); + output.writeUInt32NoTag(CodedOutputStream.computeInt32Size(1, v)); + output.writeInt32(1, v); + } + if (this.nullableBool != null) { + val v = this.nullableBool!!; + output.writeTag(5, 2); + output.writeUInt32NoTag(CodedOutputStream.computeBoolSize(1, v)); + output.writeBool(1, v); + } + } + + companion object { + val MARSHALLER: Marshaller = + object : Marshaller { + override fun stream(value: WellKnownTypesMessage): InputStream { + val bytes = ByteArray(value.getSerializedSize()) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch (e: IOException) { + throw RuntimeException(e) + } + return ByteArrayInputStream(bytes) + } + override fun parse(stream: InputStream): WellKnownTypesMessage { + try { + return WellKnownTypesMessage.parseFrom(CodedInputStream.newInstance(stream)) + } catch (e: IOException) { + throw RuntimeException(e) + } + } + } + + @Throws(IOException::class) + fun parseFrom(input: CodedInputStream): WellKnownTypesMessage { + var createdAt: Instant = Instant.EPOCH + var ttl: Duration = Duration.ZERO + var nullableString: kotlin.String? = null + var nullableInt: Int? = null + var nullableBool: kotlin.Boolean? = null + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { val _length = input.readRawVarint32(); + val _oldLimit = input.pushLimit(_length); + var _tsSeconds = 0L; + var _tsNanos = 0; + while (!input.isAtEnd()) { + val _tsTag = input.readTag() + if (WireFormat.getTagFieldNumber(_tsTag) == 1) { _tsSeconds = input.readInt64() } + else if (WireFormat.getTagFieldNumber(_tsTag) == 2) { _tsNanos = input.readInt32() } + else { input.skipField(_tsTag) } + }; + createdAt = Instant.ofEpochSecond(_tsSeconds, _tsNanos.toLong()); + input.popLimit(_oldLimit); } + else if (WireFormat.getTagFieldNumber(tag) == 2) { val _length = input.readRawVarint32(); + val _oldLimit = input.pushLimit(_length); + var _durSeconds = 0L; + var _durNanos = 0; + while (!input.isAtEnd()) { + val _durTag = input.readTag() + if (WireFormat.getTagFieldNumber(_durTag) == 1) { _durSeconds = input.readInt64() } + else if (WireFormat.getTagFieldNumber(_durTag) == 2) { _durNanos = input.readInt32() } + else { input.skipField(_durTag) } + }; + ttl = Duration.ofSeconds(_durSeconds, _durNanos.toLong()); + input.popLimit(_oldLimit); } + else if (WireFormat.getTagFieldNumber(tag) == 3) { val _length = input.readRawVarint32(); + val _oldLimit = input.pushLimit(_length); + input.readTag(); + nullableString = input.readString(); + input.popLimit(_oldLimit); } + else if (WireFormat.getTagFieldNumber(tag) == 4) { val _length = input.readRawVarint32(); + val _oldLimit = input.pushLimit(_length); + input.readTag(); + nullableInt = input.readInt32(); + input.popLimit(_oldLimit); } + else if (WireFormat.getTagFieldNumber(tag) == 5) { val _length = input.readRawVarint32(); + val _oldLimit = input.pushLimit(_length); + input.readTag(); + nullableBool = input.readBool(); + input.popLimit(_oldLimit); } + else { input.skipField(tag) } + } + return WellKnownTypesMessage(createdAt, ttl, nullableString, nullableInt, nullableBool) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin-quarkus/gradle.properties b/testers/grpc/kotlin-quarkus/gradle.properties new file mode 100644 index 0000000000..14bdf6b2b0 --- /dev/null +++ b/testers/grpc/kotlin-quarkus/gradle.properties @@ -0,0 +1 @@ +kotlin.daemon.jvmargs=-Xmx4g diff --git a/testers/grpc/kotlin-quarkus/src/test/kotlin/com/example/grpc/GrpcIntegrationTest.kt b/testers/grpc/kotlin-quarkus/src/test/kotlin/com/example/grpc/GrpcIntegrationTest.kt new file mode 100644 index 0000000000..fa18fff06f --- /dev/null +++ b/testers/grpc/kotlin-quarkus/src/test/kotlin/com/example/grpc/GrpcIntegrationTest.kt @@ -0,0 +1,448 @@ +package com.example.grpc + +import com.google.protobuf.ByteString +import io.grpc.ManagedChannel +import io.grpc.Server +import io.grpc.inprocess.InProcessChannelBuilder +import io.grpc.inprocess.InProcessServerBuilder +import io.smallrye.mutiny.Uni +import org.junit.After +import org.junit.Assert.* +import org.junit.Before +import org.junit.Test +import java.time.Duration +import java.time.Instant + +class GrpcIntegrationTest { + + private lateinit var server: Server + private lateinit var channel: ManagedChannel + private lateinit var orderClient: OrderServiceClient + private lateinit var echoClient: EchoServiceClient + + private val testCustomer = Customer(CustomerId.valueOf("CUST-123"), "John Doe", "john@example.com") + + @Before + fun setUp() { + val serverName = InProcessServerBuilder.generateName() + + val orderImpl = object : OrderService { + override fun getCustomer(request: GetCustomerRequest): Uni = + Uni.createFrom().item(GetCustomerResponse(Customer(CustomerId.valueOf(request.customerId), "John Doe", "john@example.com"))) + + override fun createOrder(request: CreateOrderRequest): Uni = + Uni.createFrom().item(CreateOrderResponse(request.order!!.orderId.unwrap(), OrderStatus.ORDER_STATUS_PENDING)) + + override fun listOrders(request: ListOrdersRequest): Iterator { + val updates = mutableListOf( + OrderUpdate("ORD-1", OrderStatus.ORDER_STATUS_PENDING, Instant.ofEpochSecond(1000, 500)), + OrderUpdate("ORD-2", OrderStatus.ORDER_STATUS_SHIPPED, Instant.ofEpochSecond(2000, 1000)), + OrderUpdate("ORD-3", OrderStatus.ORDER_STATUS_DELIVERED, Instant.ofEpochSecond(3000, 0)) + ) + return updates.iterator() + } + + override fun submitOrders(requests: Iterator): Uni = + throw UnsupportedOperationException() + + override fun chat(requests: Iterator): Iterator = + throw UnsupportedOperationException() + } + + val echoImpl = object : EchoService { + override fun echoScalarTypes(request: ScalarTypes): Uni = Uni.createFrom().item(request) + override fun echoCustomer(request: Customer): Uni = Uni.createFrom().item(request) + override fun echoOrder(request: Order): Uni = Uni.createFrom().item(request) + override fun echoInventory(request: Inventory): Uni = Uni.createFrom().item(request) + override fun echoOuter(request: Outer): Uni = Uni.createFrom().item(request) + override fun echoOptionalFields(request: OptionalFields): Uni = Uni.createFrom().item(request) + override fun echoWellKnownTypes(request: WellKnownTypesMessage): Uni = Uni.createFrom().item(request) + override fun echoPaymentMethod(request: PaymentMethod): Uni = Uni.createFrom().item(request) + override fun echoNotification(request: Notification): Uni = Uni.createFrom().item(request) + } + + server = InProcessServerBuilder + .forName(serverName) + .directExecutor() + .addService(OrderServiceServer(orderImpl)) + .addService(EchoServiceServer(echoImpl)) + .build() + .start() + + channel = InProcessChannelBuilder.forName(serverName).directExecutor().build() + orderClient = OrderServiceClient(channel) + echoClient = EchoServiceClient(channel) + } + + @After + fun tearDown() { + channel.shutdownNow() + server.shutdownNow() + } + + // ---- gRPC service tests ---- + + @Test + fun testGetCustomer() { + val response = orderClient.getCustomer(GetCustomerRequest("CUST-123")).await().indefinitely() + assertNotNull(response) + assertNotNull(response.customer) + assertEquals("CUST-123", response.customer!!.customerId.unwrap()) + assertEquals("John Doe", response.customer!!.name) + assertEquals("john@example.com", response.customer!!.email) + } + + @Test + fun testCreateOrder() { + val order = Order( + OrderId.valueOf("ORD-42"), + CustomerId.valueOf("CUST-1"), + 9999L, + Instant.ofEpochSecond(1700000000L, 123456789) + ) + + val response = orderClient.createOrder(CreateOrderRequest(order)).await().indefinitely() + assertNotNull(response) + assertEquals("ORD-42", response.orderId) + assertEquals(OrderStatus.ORDER_STATUS_PENDING, response.status) + } + + @Test + fun testListOrders() { + val updates = orderClient.listOrders(ListOrdersRequest("CUST-123", 10)) + val results = mutableListOf() + for (update in updates) { results.add(update) } + + assertEquals(3, results.size) + assertEquals("ORD-1", results[0].orderId) + assertEquals(OrderStatus.ORDER_STATUS_PENDING, results[0].status) + assertEquals("ORD-2", results[1].orderId) + assertEquals(OrderStatus.ORDER_STATUS_SHIPPED, results[1].status) + assertEquals("ORD-3", results[2].orderId) + assertEquals(OrderStatus.ORDER_STATUS_DELIVERED, results[2].status) + } + + // ---- Echo round-trip tests ---- + + @Test + fun testEchoCustomer() { + val parsed = echoClient.echoCustomer(testCustomer).await().indefinitely() + assertEquals(testCustomer, parsed) + } + + @Test + fun testEchoOrder() { + val order = Order( + OrderId.valueOf("ORD-1"), + CustomerId.valueOf("CUST-1"), + 5000L, + Instant.ofEpochSecond(1700000000L, 123456789) + ) + val parsed = echoClient.echoOrder(order).await().indefinitely() + assertEquals(order.orderId, parsed.orderId) + assertEquals(order.customerId, parsed.customerId) + assertEquals(order.amountCents, parsed.amountCents) + assertEquals(order.createdAt, parsed.createdAt) + } + + // ---- Scalar types ---- + + @Test + fun testEchoScalarTypes() { + val scalars = ScalarTypes( + 3.14, + 2.71f, + 42, + 9876543210L, + 100, + 200L, + -50, + -100L, + 999, + 888L, + -777, + -666L, + true, + "hello world", + ByteString.copyFromUtf8("binary data") + ) + val parsed = echoClient.echoScalarTypes(scalars).await().indefinitely() + assertEquals(scalars.doubleVal, parsed.doubleVal, 0.0001) + assertEquals(scalars.floatVal, parsed.floatVal, 0.0001f) + assertEquals(scalars.int32Val, parsed.int32Val) + assertEquals(scalars.int64Val, parsed.int64Val) + assertEquals(scalars.uint32Val, parsed.uint32Val) + assertEquals(scalars.uint64Val, parsed.uint64Val) + assertEquals(scalars.sint32Val, parsed.sint32Val) + assertEquals(scalars.sint64Val, parsed.sint64Val) + assertEquals(scalars.fixed32Val, parsed.fixed32Val) + assertEquals(scalars.fixed64Val, parsed.fixed64Val) + assertEquals(scalars.sfixed32Val, parsed.sfixed32Val) + assertEquals(scalars.sfixed64Val, parsed.sfixed64Val) + assertEquals(scalars.boolVal, parsed.boolVal) + assertEquals(scalars.stringVal, parsed.stringVal) + assertEquals(scalars.bytesVal, parsed.bytesVal) + } + + // ---- Enum tests ---- + + @Test + fun testEnumToValueFromValueRoundTrip() { + for (status in OrderStatus.entries) { + val wireValue = status.toValue() + val back = OrderStatus.fromValue(wireValue) + assertEquals(status, back) + } + } + + @Test + fun testEnumForce() { + assertEquals(OrderStatus.ORDER_STATUS_PENDING, OrderStatus.force("ORDER_STATUS_PENDING")) + } + + @Test(expected = RuntimeException::class) + fun testEnumForceInvalid() { + OrderStatus.force("NONEXISTENT") + } + + @Test(expected = IllegalArgumentException::class) + fun testEnumFromValueInvalid() { + OrderStatus.fromValue(999) + } + + @Test + fun testPriorityEnumRoundTrip() { + for (p in Priority.entries) { + val wireValue = p.toValue() + val back = Priority.fromValue(wireValue) + assertEquals(p, back) + } + } + + // ---- Optional fields ---- + + @Test + fun testEchoOptionalFieldsAllPresent() { + val opt = OptionalFields("Alice", 30, testCustomer) + val parsed = echoClient.echoOptionalFields(opt).await().indefinitely() + assertEquals("Alice", parsed.name) + assertEquals(30, parsed.age) + assertNotNull(parsed.customer) + assertEquals("CUST-123", parsed.customer!!.customerId.unwrap()) + } + + @Test + fun testEchoOptionalFieldsAllEmpty() { + val opt = OptionalFields(null, null, null) + val parsed = echoClient.echoOptionalFields(opt).await().indefinitely() + assertNull(parsed.name) + assertNull(parsed.age) + assertNull(parsed.customer) + } + + @Test + fun testEchoOptionalFieldsPartiallyPresent() { + val opt = OptionalFields("Bob", null, null) + val parsed = echoClient.echoOptionalFields(opt).await().indefinitely() + assertEquals("Bob", parsed.name) + assertNull(parsed.age) + assertNull(parsed.customer) + } + + // ---- Nested messages ---- + + @Test + fun testEchoOuter() { + val outer = Outer("outer-name", Inner(42, "inner-desc")) + val parsed = echoClient.echoOuter(outer).await().indefinitely() + assertEquals("outer-name", parsed.name) + assertNotNull(parsed.inner) + assertEquals(42, parsed.inner!!.value) + assertEquals("inner-desc", parsed.inner!!.description) + } + + // ---- OneOf types ---- + + @Test + fun testEchoPaymentMethodCreditCard() { + val cc = CreditCard("4111111111111111", "12/25", "123") + val method: PaymentMethodMethod = PaymentMethodMethod.CreditCardValue(cc) + val pm = PaymentMethod("PAY-1", method) + val parsed = echoClient.echoPaymentMethod(pm).await().indefinitely() + assertEquals("PAY-1", parsed.id) + assertNotNull(parsed.method) + assertTrue(parsed.method is PaymentMethodMethod.CreditCardValue) + val ccv = parsed.method as PaymentMethodMethod.CreditCardValue + assertEquals("4111111111111111", ccv.creditCard.cardNumber) + assertEquals("12/25", ccv.creditCard.expiryDate) + assertEquals("123", ccv.creditCard.cvv) + } + + @Test + fun testEchoPaymentMethodBankTransfer() { + val bt = BankTransfer("123456789", "021000021") + val method: PaymentMethodMethod = PaymentMethodMethod.BankTransferValue(bt) + val pm = PaymentMethod("PAY-2", method) + val parsed = echoClient.echoPaymentMethod(pm).await().indefinitely() + assertEquals("PAY-2", parsed.id) + assertNotNull(parsed.method) + assertTrue(parsed.method is PaymentMethodMethod.BankTransferValue) + val btv = parsed.method as PaymentMethodMethod.BankTransferValue + assertEquals("123456789", btv.bankTransfer.accountNumber) + assertEquals("021000021", btv.bankTransfer.routingNumber) + } + + @Test + fun testEchoPaymentMethodWallet() { + val w = Wallet("wallet-42", "Stripe") + val method: PaymentMethodMethod = PaymentMethodMethod.WalletValue(w) + val pm = PaymentMethod("PAY-3", method) + val parsed = echoClient.echoPaymentMethod(pm).await().indefinitely() + assertEquals("PAY-3", parsed.id) + assertNotNull(parsed.method) + assertTrue(parsed.method is PaymentMethodMethod.WalletValue) + val wv = parsed.method as PaymentMethodMethod.WalletValue + assertEquals("wallet-42", wv.wallet.walletId) + assertEquals("Stripe", wv.wallet.provider) + } + + @Test + fun testEchoNotificationWithEmailTarget() { + val notif = Notification("Hello!", Priority.PRIORITY_HIGH, NotificationTarget.Email("user@example.com")) + val parsed = echoClient.echoNotification(notif).await().indefinitely() + assertEquals("Hello!", parsed.message) + assertEquals(Priority.PRIORITY_HIGH, parsed.priority) + assertNotNull(parsed.target) + assertTrue(parsed.target is NotificationTarget.Email) + assertEquals("user@example.com", (parsed.target as NotificationTarget.Email).email) + } + + @Test + fun testEchoNotificationWithPhoneTarget() { + val notif = Notification("Alert", Priority.PRIORITY_CRITICAL, NotificationTarget.Phone("+1234567890")) + val parsed = echoClient.echoNotification(notif).await().indefinitely() + assertEquals("Alert", parsed.message) + assertEquals(Priority.PRIORITY_CRITICAL, parsed.priority) + assertNotNull(parsed.target) + assertTrue(parsed.target is NotificationTarget.Phone) + assertEquals("+1234567890", (parsed.target as NotificationTarget.Phone).phone) + } + + @Test + fun testEchoNotificationWithWebhookTarget() { + val notif = + Notification("Event", Priority.PRIORITY_LOW, NotificationTarget.WebhookUrl("https://hooks.example.com/abc")) + val parsed = echoClient.echoNotification(notif).await().indefinitely() + assertEquals("Event", parsed.message) + assertEquals(Priority.PRIORITY_LOW, parsed.priority) + assertNotNull(parsed.target) + assertTrue(parsed.target is NotificationTarget.WebhookUrl) + assertEquals("https://hooks.example.com/abc", (parsed.target as NotificationTarget.WebhookUrl).webhookUrl) + } + + // ---- Collections ---- + + @Test + fun testEchoInventory() { + val productIds = listOf("PROD-1", "PROD-2", "PROD-3") + val stockCounts = mapOf("PROD-1" to 100, "PROD-2" to 200, "PROD-3" to 0) + val orders = listOf( + Order(OrderId.valueOf("ORD-1"), CustomerId.valueOf("CUST-1"), 1000L, Instant.ofEpochSecond(1000, 0)), + Order(OrderId.valueOf("ORD-2"), CustomerId.valueOf("CUST-2"), 2000L, Instant.ofEpochSecond(2000, 0)) + ) + + val inventory = Inventory("WH-1", productIds, stockCounts, orders) + val parsed = echoClient.echoInventory(inventory).await().indefinitely() + assertEquals("WH-1", parsed.warehouseId) + assertEquals(3, parsed.productIds.size) + assertEquals("PROD-1", parsed.productIds[0]) + assertEquals("PROD-2", parsed.productIds[1]) + assertEquals("PROD-3", parsed.productIds[2]) + assertEquals(3, parsed.stockCounts.size) + assertEquals(100, parsed.stockCounts["PROD-1"]) + assertEquals(200, parsed.stockCounts["PROD-2"]) + assertEquals(0, parsed.stockCounts["PROD-3"]) + assertEquals(2, parsed.recentOrders.size) + assertEquals("ORD-1", parsed.recentOrders[0].orderId.unwrap()) + assertEquals("ORD-2", parsed.recentOrders[1].orderId.unwrap()) + } + + @Test + fun testEchoInventoryEmptyCollections() { + val inventory = Inventory("WH-EMPTY", emptyList(), emptyMap(), emptyList()) + val parsed = echoClient.echoInventory(inventory).await().indefinitely() + assertEquals("WH-EMPTY", parsed.warehouseId) + assertTrue(parsed.productIds.isEmpty()) + assertTrue(parsed.stockCounts.isEmpty()) + assertTrue(parsed.recentOrders.isEmpty()) + } + + // ---- Well-known types ---- + + @Test + fun testEchoWellKnownTypes() { + val msg = WellKnownTypesMessage( + Instant.ofEpochSecond(1700000000L, 123456789), + Duration.ofSeconds(3600, 500000000), + "hello", + 42, + true + ) + val parsed = echoClient.echoWellKnownTypes(msg).await().indefinitely() + assertEquals(msg.createdAt, parsed.createdAt) + assertEquals(msg.ttl, parsed.ttl) + assertEquals("hello", parsed.nullableString) + assertEquals(42, parsed.nullableInt) + assertEquals(true, parsed.nullableBool) + } + + // ---- Wrapper ID types ---- + + @Test + fun testCustomerIdValueOf() { + val id = CustomerId.valueOf("abc") + assertEquals("abc", id.unwrap()) + assertEquals("abc", id.toString()) + } + + @Test + fun testOrderIdValueOf() { + val id = OrderId.valueOf("ORD-1") + assertEquals("ORD-1", id.unwrap()) + assertEquals("ORD-1", id.toString()) + } + + // ---- With methods (copy) ---- + + @Test + fun testCustomerWithMethods() { + val updated = testCustomer.copy(name = "Jane Doe") + assertEquals("Jane Doe", updated.name) + assertEquals(testCustomer.customerId, updated.customerId) + assertEquals(testCustomer.email, updated.email) + } + + @Test + fun testOrderWithMethods() { + val order = Order( + OrderId.valueOf("ORD-1"), + CustomerId.valueOf("CUST-1"), + 1000L, + Instant.ofEpochSecond(1000, 0) + ) + val updated = order.copy(amountCents = 2000L) + assertEquals(2000L, updated.amountCents) + assertEquals(order.orderId, updated.orderId) + } + + // ---- Echo with empty strings ---- + + @Test + fun testEchoCustomerEmptyStrings() { + val empty = Customer(CustomerId.valueOf(""), "", "") + val parsed = echoClient.echoCustomer(empty).await().indefinitely() + assertEquals("", parsed.customerId.unwrap()) + assertEquals("", parsed.name) + assertEquals("", parsed.email) + } +} diff --git a/testers/grpc/kotlin/build.gradle.kts b/testers/grpc/kotlin/build.gradle.kts new file mode 100644 index 0000000000..08707c81d5 --- /dev/null +++ b/testers/grpc/kotlin/build.gradle.kts @@ -0,0 +1,38 @@ +plugins { + kotlin("jvm") +} + +repositories { + mavenCentral() +} + +dependencies { + implementation(project(":foundations-jdbc")) + implementation("com.google.protobuf:protobuf-java:4.29.3") + implementation("io.grpc:grpc-netty-shaded:1.69.0") + implementation("io.grpc:grpc-protobuf:1.69.0") + implementation("io.grpc:grpc-stub:1.69.0") + + testImplementation("io.grpc:grpc-testing:1.69.0") + testImplementation("io.grpc:grpc-inprocess:1.69.0") + testImplementation("junit:junit:4.13.2") +} + +sourceSets { + main { + kotlin { + srcDir("generated-and-checked-in") + srcDir("src/kotlin") + } + } + test { + kotlin { + srcDir("src/test/kotlin") + } + } +} + + +tasks.test { + useJUnit() +} diff --git a/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/BankTransfer.kt b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/BankTransfer.kt new file mode 100644 index 0000000000..89fdb44fe7 --- /dev/null +++ b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/BankTransfer.kt @@ -0,0 +1,65 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +data class BankTransfer( + val accountNumber: kotlin.String, + val routingNumber: kotlin.String +) { + fun getSerializedSize(): Int { + var size: Int = 0 + size = size + CodedOutputStream.computeStringSize(1, this.accountNumber) + size = size + CodedOutputStream.computeStringSize(2, this.routingNumber) + return size + } + + @Throws(IOException::class) + fun writeTo(output: CodedOutputStream) { + output.writeString(1, this.accountNumber) + output.writeString(2, this.routingNumber) + } + + companion object { + val MARSHALLER: Marshaller = + object : Marshaller { + override fun stream(value: BankTransfer): InputStream { + val bytes = ByteArray(value.getSerializedSize()) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch (e: IOException) { + throw RuntimeException(e) + } + return ByteArrayInputStream(bytes) + } + override fun parse(stream: InputStream): BankTransfer { + try { + return BankTransfer.parseFrom(CodedInputStream.newInstance(stream)) + } catch (e: IOException) { + throw RuntimeException(e) + } + } + } + + @Throws(IOException::class) + fun parseFrom(input: CodedInputStream): BankTransfer { + var accountNumber: kotlin.String = "" + var routingNumber: kotlin.String = "" + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { accountNumber = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 2) { routingNumber = input.readString() } + else { input.skipField(tag) } + } + return BankTransfer(accountNumber, routingNumber) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/ChatMessage.kt b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/ChatMessage.kt new file mode 100644 index 0000000000..f751ee332c --- /dev/null +++ b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/ChatMessage.kt @@ -0,0 +1,85 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException +import java.time.Instant + +data class ChatMessage( + val sender: kotlin.String, + val content: kotlin.String, + val sentAt: Instant +) { + fun getSerializedSize(): Int { + var size: Int = 0 + size = size + CodedOutputStream.computeStringSize(1, this.sender) + size = size + CodedOutputStream.computeStringSize(2, this.content) + size = size + CodedOutputStream.computeTagSize(3) + CodedOutputStream.computeUInt32SizeNoTag(CodedOutputStream.computeInt64Size(1, this.sentAt.getEpochSecond()) + CodedOutputStream.computeInt32Size(2, this.sentAt.getNano())) + CodedOutputStream.computeInt64Size(1, this.sentAt.getEpochSecond()) + CodedOutputStream.computeInt32Size(2, this.sentAt.getNano()) + return size + } + + @Throws(IOException::class) + fun writeTo(output: CodedOutputStream) { + output.writeString(1, this.sender) + output.writeString(2, this.content) + output.writeTag(3, 2) + output.writeUInt32NoTag(CodedOutputStream.computeInt64Size(1, this.sentAt.getEpochSecond()) + CodedOutputStream.computeInt32Size(2, this.sentAt.getNano())) + output.writeInt64(1, this.sentAt.getEpochSecond()) + output.writeInt32(2, this.sentAt.getNano()) + } + + companion object { + val MARSHALLER: Marshaller = + object : Marshaller { + override fun stream(value: ChatMessage): InputStream { + val bytes = ByteArray(value.getSerializedSize()) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch (e: IOException) { + throw RuntimeException(e) + } + return ByteArrayInputStream(bytes) + } + override fun parse(stream: InputStream): ChatMessage { + try { + return ChatMessage.parseFrom(CodedInputStream.newInstance(stream)) + } catch (e: IOException) { + throw RuntimeException(e) + } + } + } + + @Throws(IOException::class) + fun parseFrom(input: CodedInputStream): ChatMessage { + var sender: kotlin.String = "" + var content: kotlin.String = "" + var sentAt: Instant = Instant.EPOCH + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { sender = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 2) { content = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 3) { val _length = input.readRawVarint32(); + val _oldLimit = input.pushLimit(_length); + var _tsSeconds = 0L; + var _tsNanos = 0; + while (!input.isAtEnd()) { + val _tsTag = input.readTag() + if (WireFormat.getTagFieldNumber(_tsTag) == 1) { _tsSeconds = input.readInt64() } + else if (WireFormat.getTagFieldNumber(_tsTag) == 2) { _tsNanos = input.readInt32() } + else { input.skipField(_tsTag) } + }; + sentAt = Instant.ofEpochSecond(_tsSeconds, _tsNanos.toLong()); + input.popLimit(_oldLimit); } + else { input.skipField(tag) } + } + return ChatMessage(sender, content, sentAt) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/CreateOrderRequest.kt b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/CreateOrderRequest.kt new file mode 100644 index 0000000000..8ca493eca0 --- /dev/null +++ b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/CreateOrderRequest.kt @@ -0,0 +1,67 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +data class CreateOrderRequest(val order: Order?) { + fun getSerializedSize(): Int { + var size: Int = 0 + if ((this.order != null)) { + size = size + CodedOutputStream.computeTagSize(1) + CodedOutputStream.computeUInt32SizeNoTag(this.order.getSerializedSize()) + this.order.getSerializedSize() + } + return size + } + + @Throws(IOException::class) + fun writeTo(output: CodedOutputStream) { + if ((this.order != null)) { + output.writeTag(1, 2); + output.writeUInt32NoTag(this.order.getSerializedSize()); + this.order.writeTo(output); + } + } + + companion object { + val MARSHALLER: Marshaller = + object : Marshaller { + override fun stream(value: CreateOrderRequest): InputStream { + val bytes = ByteArray(value.getSerializedSize()) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch (e: IOException) { + throw RuntimeException(e) + } + return ByteArrayInputStream(bytes) + } + override fun parse(stream: InputStream): CreateOrderRequest { + try { + return CreateOrderRequest.parseFrom(CodedInputStream.newInstance(stream)) + } catch (e: IOException) { + throw RuntimeException(e) + } + } + } + + @Throws(IOException::class) + fun parseFrom(input: CodedInputStream): CreateOrderRequest { + var order: Order? = null + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { val _length = input.readRawVarint32(); + val _oldLimit = input.pushLimit(_length); + order = Order.parseFrom(input); + input.popLimit(_oldLimit); } + else { input.skipField(tag) } + } + return CreateOrderRequest(order) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/CreateOrderResponse.kt b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/CreateOrderResponse.kt new file mode 100644 index 0000000000..da64b08eb1 --- /dev/null +++ b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/CreateOrderResponse.kt @@ -0,0 +1,65 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +data class CreateOrderResponse( + val orderId: kotlin.String, + val status: OrderStatus +) { + fun getSerializedSize(): Int { + var size: Int = 0 + size = size + CodedOutputStream.computeStringSize(1, this.orderId) + size = size + CodedOutputStream.computeEnumSize(2, this.status.toValue()) + return size + } + + @Throws(IOException::class) + fun writeTo(output: CodedOutputStream) { + output.writeString(1, this.orderId) + output.writeEnum(2, this.status.toValue()) + } + + companion object { + val MARSHALLER: Marshaller = + object : Marshaller { + override fun stream(value: CreateOrderResponse): InputStream { + val bytes = ByteArray(value.getSerializedSize()) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch (e: IOException) { + throw RuntimeException(e) + } + return ByteArrayInputStream(bytes) + } + override fun parse(stream: InputStream): CreateOrderResponse { + try { + return CreateOrderResponse.parseFrom(CodedInputStream.newInstance(stream)) + } catch (e: IOException) { + throw RuntimeException(e) + } + } + } + + @Throws(IOException::class) + fun parseFrom(input: CodedInputStream): CreateOrderResponse { + var orderId: kotlin.String = "" + var status: OrderStatus = OrderStatus.fromValue(0) + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { orderId = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 2) { status = OrderStatus.fromValue(input.readEnum()) } + else { input.skipField(tag) } + } + return CreateOrderResponse(orderId, status) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/CreditCard.kt b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/CreditCard.kt new file mode 100644 index 0000000000..bcdeb4e3bb --- /dev/null +++ b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/CreditCard.kt @@ -0,0 +1,70 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +data class CreditCard( + val cardNumber: kotlin.String, + val expiryDate: kotlin.String, + val cvv: kotlin.String +) { + fun getSerializedSize(): Int { + var size: Int = 0 + size = size + CodedOutputStream.computeStringSize(1, this.cardNumber) + size = size + CodedOutputStream.computeStringSize(2, this.expiryDate) + size = size + CodedOutputStream.computeStringSize(3, this.cvv) + return size + } + + @Throws(IOException::class) + fun writeTo(output: CodedOutputStream) { + output.writeString(1, this.cardNumber) + output.writeString(2, this.expiryDate) + output.writeString(3, this.cvv) + } + + companion object { + val MARSHALLER: Marshaller = + object : Marshaller { + override fun stream(value: CreditCard): InputStream { + val bytes = ByteArray(value.getSerializedSize()) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch (e: IOException) { + throw RuntimeException(e) + } + return ByteArrayInputStream(bytes) + } + override fun parse(stream: InputStream): CreditCard { + try { + return CreditCard.parseFrom(CodedInputStream.newInstance(stream)) + } catch (e: IOException) { + throw RuntimeException(e) + } + } + } + + @Throws(IOException::class) + fun parseFrom(input: CodedInputStream): CreditCard { + var cardNumber: kotlin.String = "" + var expiryDate: kotlin.String = "" + var cvv: kotlin.String = "" + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { cardNumber = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 2) { expiryDate = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 3) { cvv = input.readString() } + else { input.skipField(tag) } + } + return CreditCard(cardNumber, expiryDate, cvv) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/Customer.kt b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/Customer.kt new file mode 100644 index 0000000000..f45a363edb --- /dev/null +++ b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/Customer.kt @@ -0,0 +1,70 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +data class Customer( + val customerId: CustomerId, + val name: kotlin.String, + val email: kotlin.String +) { + fun getSerializedSize(): Int { + var size: Int = 0 + size = size + CodedOutputStream.computeStringSize(1, this.customerId.unwrap()) + size = size + CodedOutputStream.computeStringSize(2, this.name) + size = size + CodedOutputStream.computeStringSize(3, this.email) + return size + } + + @Throws(IOException::class) + fun writeTo(output: CodedOutputStream) { + output.writeString(1, this.customerId.unwrap()) + output.writeString(2, this.name) + output.writeString(3, this.email) + } + + companion object { + val MARSHALLER: Marshaller = + object : Marshaller { + override fun stream(value: Customer): InputStream { + val bytes = ByteArray(value.getSerializedSize()) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch (e: IOException) { + throw RuntimeException(e) + } + return ByteArrayInputStream(bytes) + } + override fun parse(stream: InputStream): Customer { + try { + return Customer.parseFrom(CodedInputStream.newInstance(stream)) + } catch (e: IOException) { + throw RuntimeException(e) + } + } + } + + @Throws(IOException::class) + fun parseFrom(input: CodedInputStream): Customer { + var customerId: CustomerId = CustomerId.valueOf("") + var name: kotlin.String = "" + var email: kotlin.String = "" + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { customerId = CustomerId.valueOf(input.readString()) } + else if (WireFormat.getTagFieldNumber(tag) == 2) { name = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 3) { email = input.readString() } + else { input.skipField(tag) } + } + return Customer(customerId, name, email) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/CustomerId.kt b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/CustomerId.kt new file mode 100644 index 0000000000..4508ecf7c1 --- /dev/null +++ b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/CustomerId.kt @@ -0,0 +1,22 @@ +package com.example.grpc + + + +/** Wrapper type for typr.grpc.GrpcCodegen$$$Lambda/0x00007fc001118400@7b4c50bc */ +data class CustomerId(val value: kotlin.String) { + /** Get the underlying value */ + fun unwrap(): kotlin.String { + return this.value + } + + override fun toString(): kotlin.String { + return value + } + + companion object { + /** Create a CustomerId from a raw value */ + fun valueOf(v: kotlin.String): CustomerId { + return CustomerId(v) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/EchoService.kt b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/EchoService.kt new file mode 100644 index 0000000000..2e2b2a756d --- /dev/null +++ b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/EchoService.kt @@ -0,0 +1,24 @@ +package com.example.grpc + + + +/** Clean service interface for EchoService gRPC service */ +interface EchoService { + abstract fun echoCustomer(request: Customer): Customer + + abstract fun echoInventory(request: Inventory): Inventory + + abstract fun echoNotification(request: Notification): Notification + + abstract fun echoOptionalFields(request: OptionalFields): OptionalFields + + abstract fun echoOrder(request: Order): Order + + abstract fun echoOuter(request: Outer): Outer + + abstract fun echoPaymentMethod(request: PaymentMethod): PaymentMethod + + abstract fun echoScalarTypes(request: ScalarTypes): ScalarTypes + + abstract fun echoWellKnownTypes(request: WellKnownTypesMessage): WellKnownTypesMessage +} \ No newline at end of file diff --git a/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/EchoServiceClient.kt b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/EchoServiceClient.kt new file mode 100644 index 0000000000..c326c5df62 --- /dev/null +++ b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/EchoServiceClient.kt @@ -0,0 +1,66 @@ +package com.example.grpc + +import io.grpc.CallOptions +import io.grpc.Channel +import io.grpc.MethodDescriptor +import io.grpc.MethodDescriptor.MethodType +import io.grpc.stub.ClientCalls + +/** gRPC client wrapper for EchoService - wraps Channel with clean types */ +data class EchoServiceClient(val channel: Channel) : EchoService { + override fun echoCustomer(request: Customer): Customer { + return ClientCalls.blockingUnaryCall(channel, EchoServiceClient.ECHO_CUSTOMER, CallOptions.DEFAULT, request) + } + + override fun echoInventory(request: Inventory): Inventory { + return ClientCalls.blockingUnaryCall(channel, EchoServiceClient.ECHO_INVENTORY, CallOptions.DEFAULT, request) + } + + override fun echoNotification(request: Notification): Notification { + return ClientCalls.blockingUnaryCall(channel, EchoServiceClient.ECHO_NOTIFICATION, CallOptions.DEFAULT, request) + } + + override fun echoOptionalFields(request: OptionalFields): OptionalFields { + return ClientCalls.blockingUnaryCall(channel, EchoServiceClient.ECHO_OPTIONAL_FIELDS, CallOptions.DEFAULT, request) + } + + override fun echoOrder(request: Order): Order { + return ClientCalls.blockingUnaryCall(channel, EchoServiceClient.ECHO_ORDER, CallOptions.DEFAULT, request) + } + + override fun echoOuter(request: Outer): Outer { + return ClientCalls.blockingUnaryCall(channel, EchoServiceClient.ECHO_OUTER, CallOptions.DEFAULT, request) + } + + override fun echoPaymentMethod(request: PaymentMethod): PaymentMethod { + return ClientCalls.blockingUnaryCall(channel, EchoServiceClient.ECHO_PAYMENT_METHOD, CallOptions.DEFAULT, request) + } + + override fun echoScalarTypes(request: ScalarTypes): ScalarTypes { + return ClientCalls.blockingUnaryCall(channel, EchoServiceClient.ECHO_SCALAR_TYPES, CallOptions.DEFAULT, request) + } + + override fun echoWellKnownTypes(request: WellKnownTypesMessage): WellKnownTypesMessage { + return ClientCalls.blockingUnaryCall(channel, EchoServiceClient.ECHO_WELL_KNOWN_TYPES, CallOptions.DEFAULT, request) + } + + companion object { + val ECHO_CUSTOMER: MethodDescriptor = MethodDescriptor.newBuilder(Customer.MARSHALLER, Customer.MARSHALLER).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoCustomer").build() + + val ECHO_INVENTORY: MethodDescriptor = MethodDescriptor.newBuilder(Inventory.MARSHALLER, Inventory.MARSHALLER).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoInventory").build() + + val ECHO_NOTIFICATION: MethodDescriptor = MethodDescriptor.newBuilder(Notification.MARSHALLER, Notification.MARSHALLER).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoNotification").build() + + val ECHO_OPTIONAL_FIELDS: MethodDescriptor = MethodDescriptor.newBuilder(OptionalFields.MARSHALLER, OptionalFields.MARSHALLER).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoOptionalFields").build() + + val ECHO_ORDER: MethodDescriptor = MethodDescriptor.newBuilder(Order.MARSHALLER, Order.MARSHALLER).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoOrder").build() + + val ECHO_OUTER: MethodDescriptor = MethodDescriptor.newBuilder(Outer.MARSHALLER, Outer.MARSHALLER).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoOuter").build() + + val ECHO_PAYMENT_METHOD: MethodDescriptor = MethodDescriptor.newBuilder(PaymentMethod.MARSHALLER, PaymentMethod.MARSHALLER).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoPaymentMethod").build() + + val ECHO_SCALAR_TYPES: MethodDescriptor = MethodDescriptor.newBuilder(ScalarTypes.MARSHALLER, ScalarTypes.MARSHALLER).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoScalarTypes").build() + + val ECHO_WELL_KNOWN_TYPES: MethodDescriptor = MethodDescriptor.newBuilder(WellKnownTypesMessage.MARSHALLER, WellKnownTypesMessage.MARSHALLER).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoWellKnownTypes").build() + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/EchoServiceServer.kt b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/EchoServiceServer.kt new file mode 100644 index 0000000000..28e190e236 --- /dev/null +++ b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/EchoServiceServer.kt @@ -0,0 +1,43 @@ +package com.example.grpc + +import io.grpc.BindableService +import io.grpc.MethodDescriptor +import io.grpc.MethodDescriptor.MethodType +import io.grpc.ServerServiceDefinition +import io.grpc.stub.ServerCalls + +/** gRPC server adapter for EchoService - delegates to clean service interface */ +data class EchoServiceServer(val delegate: EchoService) : BindableService { + override fun bindService(): ServerServiceDefinition { + return ServerServiceDefinition.builder("testgrpc.EchoService").addMethod(EchoServiceServer.ECHO_SCALAR_TYPES, ServerCalls.asyncUnaryCall({ request, responseObserver -> responseObserver.onNext(delegate.echoScalarTypes(request)) + responseObserver.onCompleted() })).addMethod(EchoServiceServer.ECHO_CUSTOMER, ServerCalls.asyncUnaryCall({ request, responseObserver -> responseObserver.onNext(delegate.echoCustomer(request)) + responseObserver.onCompleted() })).addMethod(EchoServiceServer.ECHO_ORDER, ServerCalls.asyncUnaryCall({ request, responseObserver -> responseObserver.onNext(delegate.echoOrder(request)) + responseObserver.onCompleted() })).addMethod(EchoServiceServer.ECHO_INVENTORY, ServerCalls.asyncUnaryCall({ request, responseObserver -> responseObserver.onNext(delegate.echoInventory(request)) + responseObserver.onCompleted() })).addMethod(EchoServiceServer.ECHO_OUTER, ServerCalls.asyncUnaryCall({ request, responseObserver -> responseObserver.onNext(delegate.echoOuter(request)) + responseObserver.onCompleted() })).addMethod(EchoServiceServer.ECHO_OPTIONAL_FIELDS, ServerCalls.asyncUnaryCall({ request, responseObserver -> responseObserver.onNext(delegate.echoOptionalFields(request)) + responseObserver.onCompleted() })).addMethod(EchoServiceServer.ECHO_WELL_KNOWN_TYPES, ServerCalls.asyncUnaryCall({ request, responseObserver -> responseObserver.onNext(delegate.echoWellKnownTypes(request)) + responseObserver.onCompleted() })).addMethod(EchoServiceServer.ECHO_PAYMENT_METHOD, ServerCalls.asyncUnaryCall({ request, responseObserver -> responseObserver.onNext(delegate.echoPaymentMethod(request)) + responseObserver.onCompleted() })).addMethod(EchoServiceServer.ECHO_NOTIFICATION, ServerCalls.asyncUnaryCall({ request, responseObserver -> responseObserver.onNext(delegate.echoNotification(request)) + responseObserver.onCompleted() })).build() + } + + companion object { + val ECHO_CUSTOMER: MethodDescriptor = MethodDescriptor.newBuilder(Customer.MARSHALLER, Customer.MARSHALLER).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoCustomer").build() + + val ECHO_INVENTORY: MethodDescriptor = MethodDescriptor.newBuilder(Inventory.MARSHALLER, Inventory.MARSHALLER).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoInventory").build() + + val ECHO_NOTIFICATION: MethodDescriptor = MethodDescriptor.newBuilder(Notification.MARSHALLER, Notification.MARSHALLER).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoNotification").build() + + val ECHO_OPTIONAL_FIELDS: MethodDescriptor = MethodDescriptor.newBuilder(OptionalFields.MARSHALLER, OptionalFields.MARSHALLER).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoOptionalFields").build() + + val ECHO_ORDER: MethodDescriptor = MethodDescriptor.newBuilder(Order.MARSHALLER, Order.MARSHALLER).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoOrder").build() + + val ECHO_OUTER: MethodDescriptor = MethodDescriptor.newBuilder(Outer.MARSHALLER, Outer.MARSHALLER).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoOuter").build() + + val ECHO_PAYMENT_METHOD: MethodDescriptor = MethodDescriptor.newBuilder(PaymentMethod.MARSHALLER, PaymentMethod.MARSHALLER).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoPaymentMethod").build() + + val ECHO_SCALAR_TYPES: MethodDescriptor = MethodDescriptor.newBuilder(ScalarTypes.MARSHALLER, ScalarTypes.MARSHALLER).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoScalarTypes").build() + + val ECHO_WELL_KNOWN_TYPES: MethodDescriptor = MethodDescriptor.newBuilder(WellKnownTypesMessage.MARSHALLER, WellKnownTypesMessage.MARSHALLER).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoWellKnownTypes").build() + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/GetCustomerRequest.kt b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/GetCustomerRequest.kt new file mode 100644 index 0000000000..60593bbfc7 --- /dev/null +++ b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/GetCustomerRequest.kt @@ -0,0 +1,58 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +data class GetCustomerRequest(val customerId: kotlin.String) { + fun getSerializedSize(): Int { + var size: Int = 0 + size = size + CodedOutputStream.computeStringSize(1, this.customerId) + return size + } + + @Throws(IOException::class) + fun writeTo(output: CodedOutputStream) { + output.writeString(1, this.customerId) + } + + companion object { + val MARSHALLER: Marshaller = + object : Marshaller { + override fun stream(value: GetCustomerRequest): InputStream { + val bytes = ByteArray(value.getSerializedSize()) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch (e: IOException) { + throw RuntimeException(e) + } + return ByteArrayInputStream(bytes) + } + override fun parse(stream: InputStream): GetCustomerRequest { + try { + return GetCustomerRequest.parseFrom(CodedInputStream.newInstance(stream)) + } catch (e: IOException) { + throw RuntimeException(e) + } + } + } + + @Throws(IOException::class) + fun parseFrom(input: CodedInputStream): GetCustomerRequest { + var customerId: kotlin.String = "" + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { customerId = input.readString() } + else { input.skipField(tag) } + } + return GetCustomerRequest(customerId) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/GetCustomerResponse.kt b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/GetCustomerResponse.kt new file mode 100644 index 0000000000..325632d08f --- /dev/null +++ b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/GetCustomerResponse.kt @@ -0,0 +1,67 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +data class GetCustomerResponse(val customer: Customer?) { + fun getSerializedSize(): Int { + var size: Int = 0 + if ((this.customer != null)) { + size = size + CodedOutputStream.computeTagSize(1) + CodedOutputStream.computeUInt32SizeNoTag(this.customer.getSerializedSize()) + this.customer.getSerializedSize() + } + return size + } + + @Throws(IOException::class) + fun writeTo(output: CodedOutputStream) { + if ((this.customer != null)) { + output.writeTag(1, 2); + output.writeUInt32NoTag(this.customer.getSerializedSize()); + this.customer.writeTo(output); + } + } + + companion object { + val MARSHALLER: Marshaller = + object : Marshaller { + override fun stream(value: GetCustomerResponse): InputStream { + val bytes = ByteArray(value.getSerializedSize()) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch (e: IOException) { + throw RuntimeException(e) + } + return ByteArrayInputStream(bytes) + } + override fun parse(stream: InputStream): GetCustomerResponse { + try { + return GetCustomerResponse.parseFrom(CodedInputStream.newInstance(stream)) + } catch (e: IOException) { + throw RuntimeException(e) + } + } + } + + @Throws(IOException::class) + fun parseFrom(input: CodedInputStream): GetCustomerResponse { + var customer: Customer? = null + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { val _length = input.readRawVarint32(); + val _oldLimit = input.pushLimit(_length); + customer = Customer.parseFrom(input); + input.popLimit(_oldLimit); } + else { input.skipField(tag) } + } + return GetCustomerResponse(customer) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/Inner.kt b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/Inner.kt new file mode 100644 index 0000000000..4665ed427f --- /dev/null +++ b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/Inner.kt @@ -0,0 +1,65 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +data class Inner( + val value: Int, + val description: kotlin.String +) { + fun getSerializedSize(): Int { + var size: Int = 0 + size = size + CodedOutputStream.computeInt32Size(1, this.value) + size = size + CodedOutputStream.computeStringSize(2, this.description) + return size + } + + @Throws(IOException::class) + fun writeTo(output: CodedOutputStream) { + output.writeInt32(1, this.value) + output.writeString(2, this.description) + } + + companion object { + val MARSHALLER: Marshaller = + object : Marshaller { + override fun stream(value: Inner): InputStream { + val bytes = ByteArray(value.getSerializedSize()) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch (e: IOException) { + throw RuntimeException(e) + } + return ByteArrayInputStream(bytes) + } + override fun parse(stream: InputStream): Inner { + try { + return Inner.parseFrom(CodedInputStream.newInstance(stream)) + } catch (e: IOException) { + throw RuntimeException(e) + } + } + } + + @Throws(IOException::class) + fun parseFrom(input: CodedInputStream): Inner { + var value: Int = 0 + var description: kotlin.String = "" + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { value = input.readInt32() } + else if (WireFormat.getTagFieldNumber(tag) == 2) { description = input.readString() } + else { input.skipField(tag) } + } + return Inner(value, description) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/Inventory.kt b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/Inventory.kt new file mode 100644 index 0000000000..7c44f2ee04 --- /dev/null +++ b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/Inventory.kt @@ -0,0 +1,110 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException +import java.util.ArrayList +import kotlin.collections.List +import kotlin.collections.Map +import kotlin.collections.MutableMap + +data class Inventory( + val warehouseId: kotlin.String, + val productIds: List, + val stockCounts: Map, + val recentOrders: List +) { + fun getSerializedSize(): Int { + var size: Int = 0 + size = size + CodedOutputStream.computeStringSize(1, this.warehouseId) + for (elem: kotlin.String in this.productIds) { + size = size + CodedOutputStream.computeStringSize(2, elem) + } + for ((k, v) in this.stockCounts) { + size = size + CodedOutputStream.computeTagSize(3) + CodedOutputStream.computeUInt32SizeNoTag(CodedOutputStream.computeStringSize(1, k) + CodedOutputStream.computeInt32Size(2, v)) + CodedOutputStream.computeStringSize(1, k) + CodedOutputStream.computeInt32Size(2, v); + } + for (elem: Order in this.recentOrders) { + size = size + CodedOutputStream.computeTagSize(4) + CodedOutputStream.computeUInt32SizeNoTag(elem.getSerializedSize()) + elem.getSerializedSize() + } + return size + } + + @Throws(IOException::class) + fun writeTo(output: CodedOutputStream) { + output.writeString(1, this.warehouseId) + for (elem: kotlin.String in this.productIds) { + output.writeString(2, elem) + } + for ((k, v) in this.stockCounts) { + output.writeTag(3, 2); + output.writeUInt32NoTag(CodedOutputStream.computeStringSize(1, k) + CodedOutputStream.computeInt32Size(2, v)); + output.writeString(1, k); + output.writeInt32(2, v); + } + for (elem: Order in this.recentOrders) { + output.writeTag(4, 2) + output.writeUInt32NoTag(elem.getSerializedSize()) + elem.writeTo(output) + } + } + + companion object { + val MARSHALLER: Marshaller = + object : Marshaller { + override fun stream(value: Inventory): InputStream { + val bytes = ByteArray(value.getSerializedSize()) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch (e: IOException) { + throw RuntimeException(e) + } + return ByteArrayInputStream(bytes) + } + override fun parse(stream: InputStream): Inventory { + try { + return Inventory.parseFrom(CodedInputStream.newInstance(stream)) + } catch (e: IOException) { + throw RuntimeException(e) + } + } + } + + @Throws(IOException::class) + fun parseFrom(input: CodedInputStream): Inventory { + var warehouseId: kotlin.String = "" + var productIds: ArrayList = ArrayList() + var stockCounts: MutableMap = mutableMapOf() + var recentOrders: ArrayList = ArrayList() + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { warehouseId = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 2) { productIds.add(input.readString()) } + else if (WireFormat.getTagFieldNumber(tag) == 3) { val length = input.readRawVarint32(); + val oldLimit = input.pushLimit(length); + var mapKey = ""; + var mapValue = 0; + while (!input.isAtEnd()) { + val entryTag = input.readTag() + if (WireFormat.getTagFieldNumber(entryTag) == 1) { mapKey = input.readString() } + else if (WireFormat.getTagFieldNumber(entryTag) == 2) { mapValue = input.readInt32() } + else { input.skipField(entryTag) } + }; + input.popLimit(oldLimit); + stockCounts[mapKey] = mapValue; } + else if (WireFormat.getTagFieldNumber(tag) == 4) { val _length = input.readRawVarint32(); + val _oldLimit = input.pushLimit(_length); + recentOrders.add(Order.parseFrom(input)); + input.popLimit(_oldLimit); } + else { input.skipField(tag) } + } + return Inventory(warehouseId, productIds, stockCounts.toMap(), recentOrders) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/ListOrdersRequest.kt b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/ListOrdersRequest.kt new file mode 100644 index 0000000000..4be1f14759 --- /dev/null +++ b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/ListOrdersRequest.kt @@ -0,0 +1,65 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +data class ListOrdersRequest( + val customerId: kotlin.String, + val pageSize: Int +) { + fun getSerializedSize(): Int { + var size: Int = 0 + size = size + CodedOutputStream.computeStringSize(1, this.customerId) + size = size + CodedOutputStream.computeInt32Size(2, this.pageSize) + return size + } + + @Throws(IOException::class) + fun writeTo(output: CodedOutputStream) { + output.writeString(1, this.customerId) + output.writeInt32(2, this.pageSize) + } + + companion object { + val MARSHALLER: Marshaller = + object : Marshaller { + override fun stream(value: ListOrdersRequest): InputStream { + val bytes = ByteArray(value.getSerializedSize()) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch (e: IOException) { + throw RuntimeException(e) + } + return ByteArrayInputStream(bytes) + } + override fun parse(stream: InputStream): ListOrdersRequest { + try { + return ListOrdersRequest.parseFrom(CodedInputStream.newInstance(stream)) + } catch (e: IOException) { + throw RuntimeException(e) + } + } + } + + @Throws(IOException::class) + fun parseFrom(input: CodedInputStream): ListOrdersRequest { + var customerId: kotlin.String = "" + var pageSize: Int = 0 + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { customerId = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 2) { pageSize = input.readInt32() } + else { input.skipField(tag) } + } + return ListOrdersRequest(customerId, pageSize) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/Notification.kt b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/Notification.kt new file mode 100644 index 0000000000..5a1836190f --- /dev/null +++ b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/Notification.kt @@ -0,0 +1,85 @@ +package com.example.grpc + +import com.example.grpc.NotificationTarget.Email +import com.example.grpc.NotificationTarget.Phone +import com.example.grpc.NotificationTarget.WebhookUrl +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +data class Notification( + val message: kotlin.String, + val priority: Priority, + val target: NotificationTarget? +) { + fun getSerializedSize(): Int { + var size: Int = 0 + size = size + CodedOutputStream.computeStringSize(1, this.message) + size = size + CodedOutputStream.computeEnumSize(2, this.priority.toValue()) + when (val __r = this.target) { + null -> {} + is Email -> { val c = __r; size = size + CodedOutputStream.computeStringSize(3, c.email) } + is Phone -> { val c = __r; size = size + CodedOutputStream.computeStringSize(4, c.phone) } + is WebhookUrl -> { val c = __r; size = size + CodedOutputStream.computeStringSize(5, c.webhookUrl) } + } + return size + } + + @Throws(IOException::class) + fun writeTo(output: CodedOutputStream) { + output.writeString(1, this.message) + output.writeEnum(2, this.priority.toValue()) + when (val __r = this.target) { + null -> {} + is Email -> { val c = __r; output.writeString(3, c.email) } + is Phone -> { val c = __r; output.writeString(4, c.phone) } + is WebhookUrl -> { val c = __r; output.writeString(5, c.webhookUrl) } + } + } + + companion object { + val MARSHALLER: Marshaller = + object : Marshaller { + override fun stream(value: Notification): InputStream { + val bytes = ByteArray(value.getSerializedSize()) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch (e: IOException) { + throw RuntimeException(e) + } + return ByteArrayInputStream(bytes) + } + override fun parse(stream: InputStream): Notification { + try { + return Notification.parseFrom(CodedInputStream.newInstance(stream)) + } catch (e: IOException) { + throw RuntimeException(e) + } + } + } + + @Throws(IOException::class) + fun parseFrom(input: CodedInputStream): Notification { + var message: kotlin.String = "" + var priority: Priority = Priority.fromValue(0) + var target: NotificationTarget? = null + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { message = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 2) { priority = Priority.fromValue(input.readEnum()) } + else if (WireFormat.getTagFieldNumber(tag) == 3) { target = Email(input.readString()) } + else if (WireFormat.getTagFieldNumber(tag) == 4) { target = Phone(input.readString()) } + else if (WireFormat.getTagFieldNumber(tag) == 5) { target = WebhookUrl(input.readString()) } + else { input.skipField(tag) } + } + return Notification(message, priority, target) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/NotificationTarget.kt b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/NotificationTarget.kt new file mode 100644 index 0000000000..5e9aa20f37 --- /dev/null +++ b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/NotificationTarget.kt @@ -0,0 +1,12 @@ +package com.example.grpc + + + +/** OneOf type for target */ +sealed interface NotificationTarget { + data class Email(val email: kotlin.String) : NotificationTarget + + data class Phone(val phone: kotlin.String) : NotificationTarget + + data class WebhookUrl(val webhookUrl: kotlin.String) : NotificationTarget +} \ No newline at end of file diff --git a/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/OptionalFields.kt b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/OptionalFields.kt new file mode 100644 index 0000000000..1885ecb5e5 --- /dev/null +++ b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/OptionalFields.kt @@ -0,0 +1,93 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +data class OptionalFields( + val name: kotlin.String?, + val age: Int?, + val customer: Customer? +) { + fun getSerializedSize(): Int { + var size: Int = 0 + if (this.name != null) { + val v = this.name!!; + size = size + CodedOutputStream.computeStringSize(1, v); + } + if (this.age != null) { + val v = this.age!!; + size = size + CodedOutputStream.computeInt32Size(2, v); + } + if (this.customer != null) { + val v = this.customer!!; + size = size + CodedOutputStream.computeTagSize(3) + CodedOutputStream.computeUInt32SizeNoTag(v.getSerializedSize()) + v.getSerializedSize(); + } + return size + } + + @Throws(IOException::class) + fun writeTo(output: CodedOutputStream) { + if (this.name != null) { + val v = this.name!!; + output.writeString(1, v); + } + if (this.age != null) { + val v = this.age!!; + output.writeInt32(2, v); + } + if (this.customer != null) { + val v = this.customer!!; + output.writeTag(3, 2); + output.writeUInt32NoTag(v.getSerializedSize()); + v.writeTo(output); + } + } + + companion object { + val MARSHALLER: Marshaller = + object : Marshaller { + override fun stream(value: OptionalFields): InputStream { + val bytes = ByteArray(value.getSerializedSize()) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch (e: IOException) { + throw RuntimeException(e) + } + return ByteArrayInputStream(bytes) + } + override fun parse(stream: InputStream): OptionalFields { + try { + return OptionalFields.parseFrom(CodedInputStream.newInstance(stream)) + } catch (e: IOException) { + throw RuntimeException(e) + } + } + } + + @Throws(IOException::class) + fun parseFrom(input: CodedInputStream): OptionalFields { + var name: kotlin.String? = null + var age: Int? = null + var customer: Customer? = null + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { name = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 2) { age = input.readInt32() } + else if (WireFormat.getTagFieldNumber(tag) == 3) { val _length = input.readRawVarint32(); + val _oldLimit = input.pushLimit(_length); + customer = Customer.parseFrom(input); + input.popLimit(_oldLimit); } + else { input.skipField(tag) } + } + return OptionalFields(name, age, customer) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/Order.kt b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/Order.kt new file mode 100644 index 0000000000..8c3600fc2b --- /dev/null +++ b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/Order.kt @@ -0,0 +1,90 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException +import java.time.Instant + +data class Order( + val orderId: OrderId, + val customerId: CustomerId, + val amountCents: kotlin.Long, + val createdAt: Instant +) { + fun getSerializedSize(): Int { + var size: Int = 0 + size = size + CodedOutputStream.computeStringSize(1, this.orderId.unwrap()) + size = size + CodedOutputStream.computeStringSize(2, this.customerId.unwrap()) + size = size + CodedOutputStream.computeInt64Size(3, this.amountCents) + size = size + CodedOutputStream.computeTagSize(4) + CodedOutputStream.computeUInt32SizeNoTag(CodedOutputStream.computeInt64Size(1, this.createdAt.getEpochSecond()) + CodedOutputStream.computeInt32Size(2, this.createdAt.getNano())) + CodedOutputStream.computeInt64Size(1, this.createdAt.getEpochSecond()) + CodedOutputStream.computeInt32Size(2, this.createdAt.getNano()) + return size + } + + @Throws(IOException::class) + fun writeTo(output: CodedOutputStream) { + output.writeString(1, this.orderId.unwrap()) + output.writeString(2, this.customerId.unwrap()) + output.writeInt64(3, this.amountCents) + output.writeTag(4, 2) + output.writeUInt32NoTag(CodedOutputStream.computeInt64Size(1, this.createdAt.getEpochSecond()) + CodedOutputStream.computeInt32Size(2, this.createdAt.getNano())) + output.writeInt64(1, this.createdAt.getEpochSecond()) + output.writeInt32(2, this.createdAt.getNano()) + } + + companion object { + val MARSHALLER: Marshaller = + object : Marshaller { + override fun stream(value: Order): InputStream { + val bytes = ByteArray(value.getSerializedSize()) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch (e: IOException) { + throw RuntimeException(e) + } + return ByteArrayInputStream(bytes) + } + override fun parse(stream: InputStream): Order { + try { + return Order.parseFrom(CodedInputStream.newInstance(stream)) + } catch (e: IOException) { + throw RuntimeException(e) + } + } + } + + @Throws(IOException::class) + fun parseFrom(input: CodedInputStream): Order { + var orderId: OrderId = OrderId.valueOf("") + var customerId: CustomerId = CustomerId.valueOf("") + var amountCents: kotlin.Long = 0L + var createdAt: Instant = Instant.EPOCH + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { orderId = OrderId.valueOf(input.readString()) } + else if (WireFormat.getTagFieldNumber(tag) == 2) { customerId = CustomerId.valueOf(input.readString()) } + else if (WireFormat.getTagFieldNumber(tag) == 3) { amountCents = input.readInt64() } + else if (WireFormat.getTagFieldNumber(tag) == 4) { val _length = input.readRawVarint32(); + val _oldLimit = input.pushLimit(_length); + var _tsSeconds = 0L; + var _tsNanos = 0; + while (!input.isAtEnd()) { + val _tsTag = input.readTag() + if (WireFormat.getTagFieldNumber(_tsTag) == 1) { _tsSeconds = input.readInt64() } + else if (WireFormat.getTagFieldNumber(_tsTag) == 2) { _tsNanos = input.readInt32() } + else { input.skipField(_tsTag) } + }; + createdAt = Instant.ofEpochSecond(_tsSeconds, _tsNanos.toLong()); + input.popLimit(_oldLimit); } + else { input.skipField(tag) } + } + return Order(orderId, customerId, amountCents, createdAt) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/OrderId.kt b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/OrderId.kt new file mode 100644 index 0000000000..04421d8cb1 --- /dev/null +++ b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/OrderId.kt @@ -0,0 +1,22 @@ +package com.example.grpc + + + +/** Wrapper type for typr.grpc.GrpcCodegen$$$Lambda/0x00007fc001118400@5884a914 */ +data class OrderId(val value: kotlin.String) { + /** Get the underlying value */ + fun unwrap(): kotlin.String { + return this.value + } + + override fun toString(): kotlin.String { + return value + } + + companion object { + /** Create a OrderId from a raw value */ + fun valueOf(v: kotlin.String): OrderId { + return OrderId(v) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/OrderService.kt b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/OrderService.kt new file mode 100644 index 0000000000..2787b730a2 --- /dev/null +++ b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/OrderService.kt @@ -0,0 +1,16 @@ +package com.example.grpc + +import kotlin.collections.Iterator + +/** Clean service interface for OrderService gRPC service */ +interface OrderService { + abstract fun chat(requests: Iterator): Iterator + + abstract fun createOrder(request: CreateOrderRequest): CreateOrderResponse + + abstract fun getCustomer(request: GetCustomerRequest): GetCustomerResponse + + abstract fun listOrders(request: ListOrdersRequest): Iterator + + abstract fun submitOrders(requests: Iterator): OrderSummary +} \ No newline at end of file diff --git a/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/OrderServiceClient.kt b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/OrderServiceClient.kt new file mode 100644 index 0000000000..ff08688b57 --- /dev/null +++ b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/OrderServiceClient.kt @@ -0,0 +1,44 @@ +package com.example.grpc + +import io.grpc.CallOptions +import io.grpc.Channel +import io.grpc.MethodDescriptor +import io.grpc.MethodDescriptor.MethodType +import io.grpc.stub.ClientCalls +import java.lang.UnsupportedOperationException +import kotlin.collections.Iterator + +/** gRPC client wrapper for OrderService - wraps Channel with clean types */ +data class OrderServiceClient(val channel: Channel) : OrderService { + override fun chat(requests: Iterator): Iterator { + throw UnsupportedOperationException("Bidi streaming not yet implemented in client wrapper") + } + + override fun createOrder(request: CreateOrderRequest): CreateOrderResponse { + return ClientCalls.blockingUnaryCall(channel, OrderServiceClient.CREATE_ORDER, CallOptions.DEFAULT, request) + } + + override fun getCustomer(request: GetCustomerRequest): GetCustomerResponse { + return ClientCalls.blockingUnaryCall(channel, OrderServiceClient.GET_CUSTOMER, CallOptions.DEFAULT, request) + } + + override fun listOrders(request: ListOrdersRequest): Iterator { + return ClientCalls.blockingServerStreamingCall(channel, OrderServiceClient.LIST_ORDERS, CallOptions.DEFAULT, request) + } + + override fun submitOrders(requests: Iterator): OrderSummary { + throw UnsupportedOperationException("Client streaming not yet implemented in client wrapper") + } + + companion object { + val CHAT: MethodDescriptor = MethodDescriptor.newBuilder(ChatMessage.MARSHALLER, ChatMessage.MARSHALLER).setType(MethodType.BIDI_STREAMING).setFullMethodName("testgrpc.OrderService/Chat").build() + + val CREATE_ORDER: MethodDescriptor = MethodDescriptor.newBuilder(CreateOrderRequest.MARSHALLER, CreateOrderResponse.MARSHALLER).setType(MethodType.UNARY).setFullMethodName("testgrpc.OrderService/CreateOrder").build() + + val GET_CUSTOMER: MethodDescriptor = MethodDescriptor.newBuilder(GetCustomerRequest.MARSHALLER, GetCustomerResponse.MARSHALLER).setType(MethodType.UNARY).setFullMethodName("testgrpc.OrderService/GetCustomer").build() + + val LIST_ORDERS: MethodDescriptor = MethodDescriptor.newBuilder(ListOrdersRequest.MARSHALLER, OrderUpdate.MARSHALLER).setType(MethodType.SERVER_STREAMING).setFullMethodName("testgrpc.OrderService/ListOrders").build() + + val SUBMIT_ORDERS: MethodDescriptor = MethodDescriptor.newBuilder(CreateOrderRequest.MARSHALLER, OrderSummary.MARSHALLER).setType(MethodType.CLIENT_STREAMING).setFullMethodName("testgrpc.OrderService/SubmitOrders").build() + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/OrderServiceServer.kt b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/OrderServiceServer.kt new file mode 100644 index 0000000000..892bd24ee7 --- /dev/null +++ b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/OrderServiceServer.kt @@ -0,0 +1,33 @@ +package com.example.grpc + +import io.grpc.BindableService +import io.grpc.MethodDescriptor +import io.grpc.MethodDescriptor.MethodType +import io.grpc.ServerServiceDefinition +import io.grpc.stub.ServerCalls +import java.lang.UnsupportedOperationException + +/** gRPC server adapter for OrderService - delegates to clean service interface */ +data class OrderServiceServer(val delegate: OrderService) : BindableService { + override fun bindService(): ServerServiceDefinition { + return ServerServiceDefinition.builder("testgrpc.OrderService").addMethod(OrderServiceServer.GET_CUSTOMER, ServerCalls.asyncUnaryCall({ request, responseObserver -> responseObserver.onNext(delegate.getCustomer(request)) + responseObserver.onCompleted() })).addMethod(OrderServiceServer.CREATE_ORDER, ServerCalls.asyncUnaryCall({ request, responseObserver -> responseObserver.onNext(delegate.createOrder(request)) + responseObserver.onCompleted() })).addMethod(OrderServiceServer.LIST_ORDERS, ServerCalls.asyncServerStreamingCall({ request, responseObserver -> val results = delegate.listOrders(request) + while (results.hasNext()) { + responseObserver.onNext(results.next()) + } + responseObserver.onCompleted() })).addMethod(OrderServiceServer.SUBMIT_ORDERS, ServerCalls.asyncClientStreamingCall({ responseObserver -> throw UnsupportedOperationException("Client streaming not yet implemented in server adapter") })).addMethod(OrderServiceServer.CHAT, ServerCalls.asyncBidiStreamingCall({ responseObserver -> throw UnsupportedOperationException("Bidi streaming not yet implemented in server adapter") })).build() + } + + companion object { + val CHAT: MethodDescriptor = MethodDescriptor.newBuilder(ChatMessage.MARSHALLER, ChatMessage.MARSHALLER).setType(MethodType.BIDI_STREAMING).setFullMethodName("testgrpc.OrderService/Chat").build() + + val CREATE_ORDER: MethodDescriptor = MethodDescriptor.newBuilder(CreateOrderRequest.MARSHALLER, CreateOrderResponse.MARSHALLER).setType(MethodType.UNARY).setFullMethodName("testgrpc.OrderService/CreateOrder").build() + + val GET_CUSTOMER: MethodDescriptor = MethodDescriptor.newBuilder(GetCustomerRequest.MARSHALLER, GetCustomerResponse.MARSHALLER).setType(MethodType.UNARY).setFullMethodName("testgrpc.OrderService/GetCustomer").build() + + val LIST_ORDERS: MethodDescriptor = MethodDescriptor.newBuilder(ListOrdersRequest.MARSHALLER, OrderUpdate.MARSHALLER).setType(MethodType.SERVER_STREAMING).setFullMethodName("testgrpc.OrderService/ListOrders").build() + + val SUBMIT_ORDERS: MethodDescriptor = MethodDescriptor.newBuilder(CreateOrderRequest.MARSHALLER, OrderSummary.MARSHALLER).setType(MethodType.CLIENT_STREAMING).setFullMethodName("testgrpc.OrderService/SubmitOrders").build() + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/OrderStatus.kt b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/OrderStatus.kt new file mode 100644 index 0000000000..5c0bc5f498 --- /dev/null +++ b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/OrderStatus.kt @@ -0,0 +1,39 @@ +package com.example.grpc + +import java.lang.IllegalArgumentException + +enum class OrderStatus(val value: kotlin.String) { + ORDER_STATUS_UNSPECIFIED("ORDER_STATUS_UNSPECIFIED"), + ORDER_STATUS_PENDING("ORDER_STATUS_PENDING"), + ORDER_STATUS_PROCESSING("ORDER_STATUS_PROCESSING"), + ORDER_STATUS_SHIPPED("ORDER_STATUS_SHIPPED"), + ORDER_STATUS_DELIVERED("ORDER_STATUS_DELIVERED"), + ORDER_STATUS_CANCELLED("ORDER_STATUS_CANCELLED"); + + fun toValue(): Int { + if (this.toString().equals("ORDER_STATUS_UNSPECIFIED")) { return 0 } + else if (this.toString().equals("ORDER_STATUS_PENDING")) { return 1 } + else if (this.toString().equals("ORDER_STATUS_PROCESSING")) { return 2 } + else if (this.toString().equals("ORDER_STATUS_SHIPPED")) { return 3 } + else if (this.toString().equals("ORDER_STATUS_DELIVERED")) { return 4 } + else if (this.toString().equals("ORDER_STATUS_CANCELLED")) { return 5 } + else { return 0 } + } + + companion object { + val Names: kotlin.String = entries.joinToString(", ") { it.value } + val ByName: kotlin.collections.Map = entries.associateBy { it.value } + fun fromValue(value: Int): OrderStatus { + if (value == 0) { return OrderStatus.ORDER_STATUS_UNSPECIFIED } + else if (value == 1) { return OrderStatus.ORDER_STATUS_PENDING } + else if (value == 2) { return OrderStatus.ORDER_STATUS_PROCESSING } + else if (value == 3) { return OrderStatus.ORDER_STATUS_SHIPPED } + else if (value == 4) { return OrderStatus.ORDER_STATUS_DELIVERED } + else if (value == 5) { return OrderStatus.ORDER_STATUS_CANCELLED } + else { throw IllegalArgumentException("Unknown enum value: " + value) } + } + + fun force(str: kotlin.String): OrderStatus = + ByName[str] ?: throw RuntimeException("'$str' does not match any of the following legal values: $Names") + } +} diff --git a/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/OrderSummary.kt b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/OrderSummary.kt new file mode 100644 index 0000000000..34645e077f --- /dev/null +++ b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/OrderSummary.kt @@ -0,0 +1,65 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +data class OrderSummary( + val totalOrders: Int, + val totalAmountCents: kotlin.Long +) { + fun getSerializedSize(): Int { + var size: Int = 0 + size = size + CodedOutputStream.computeInt32Size(1, this.totalOrders) + size = size + CodedOutputStream.computeInt64Size(2, this.totalAmountCents) + return size + } + + @Throws(IOException::class) + fun writeTo(output: CodedOutputStream) { + output.writeInt32(1, this.totalOrders) + output.writeInt64(2, this.totalAmountCents) + } + + companion object { + val MARSHALLER: Marshaller = + object : Marshaller { + override fun stream(value: OrderSummary): InputStream { + val bytes = ByteArray(value.getSerializedSize()) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch (e: IOException) { + throw RuntimeException(e) + } + return ByteArrayInputStream(bytes) + } + override fun parse(stream: InputStream): OrderSummary { + try { + return OrderSummary.parseFrom(CodedInputStream.newInstance(stream)) + } catch (e: IOException) { + throw RuntimeException(e) + } + } + } + + @Throws(IOException::class) + fun parseFrom(input: CodedInputStream): OrderSummary { + var totalOrders: Int = 0 + var totalAmountCents: kotlin.Long = 0L + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { totalOrders = input.readInt32() } + else if (WireFormat.getTagFieldNumber(tag) == 2) { totalAmountCents = input.readInt64() } + else { input.skipField(tag) } + } + return OrderSummary(totalOrders, totalAmountCents) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/OrderUpdate.kt b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/OrderUpdate.kt new file mode 100644 index 0000000000..30097bf1f2 --- /dev/null +++ b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/OrderUpdate.kt @@ -0,0 +1,85 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException +import java.time.Instant + +data class OrderUpdate( + val orderId: kotlin.String, + val status: OrderStatus, + val updatedAt: Instant +) { + fun getSerializedSize(): Int { + var size: Int = 0 + size = size + CodedOutputStream.computeStringSize(1, this.orderId) + size = size + CodedOutputStream.computeEnumSize(2, this.status.toValue()) + size = size + CodedOutputStream.computeTagSize(3) + CodedOutputStream.computeUInt32SizeNoTag(CodedOutputStream.computeInt64Size(1, this.updatedAt.getEpochSecond()) + CodedOutputStream.computeInt32Size(2, this.updatedAt.getNano())) + CodedOutputStream.computeInt64Size(1, this.updatedAt.getEpochSecond()) + CodedOutputStream.computeInt32Size(2, this.updatedAt.getNano()) + return size + } + + @Throws(IOException::class) + fun writeTo(output: CodedOutputStream) { + output.writeString(1, this.orderId) + output.writeEnum(2, this.status.toValue()) + output.writeTag(3, 2) + output.writeUInt32NoTag(CodedOutputStream.computeInt64Size(1, this.updatedAt.getEpochSecond()) + CodedOutputStream.computeInt32Size(2, this.updatedAt.getNano())) + output.writeInt64(1, this.updatedAt.getEpochSecond()) + output.writeInt32(2, this.updatedAt.getNano()) + } + + companion object { + val MARSHALLER: Marshaller = + object : Marshaller { + override fun stream(value: OrderUpdate): InputStream { + val bytes = ByteArray(value.getSerializedSize()) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch (e: IOException) { + throw RuntimeException(e) + } + return ByteArrayInputStream(bytes) + } + override fun parse(stream: InputStream): OrderUpdate { + try { + return OrderUpdate.parseFrom(CodedInputStream.newInstance(stream)) + } catch (e: IOException) { + throw RuntimeException(e) + } + } + } + + @Throws(IOException::class) + fun parseFrom(input: CodedInputStream): OrderUpdate { + var orderId: kotlin.String = "" + var status: OrderStatus = OrderStatus.fromValue(0) + var updatedAt: Instant = Instant.EPOCH + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { orderId = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 2) { status = OrderStatus.fromValue(input.readEnum()) } + else if (WireFormat.getTagFieldNumber(tag) == 3) { val _length = input.readRawVarint32(); + val _oldLimit = input.pushLimit(_length); + var _tsSeconds = 0L; + var _tsNanos = 0; + while (!input.isAtEnd()) { + val _tsTag = input.readTag() + if (WireFormat.getTagFieldNumber(_tsTag) == 1) { _tsSeconds = input.readInt64() } + else if (WireFormat.getTagFieldNumber(_tsTag) == 2) { _tsNanos = input.readInt32() } + else { input.skipField(_tsTag) } + }; + updatedAt = Instant.ofEpochSecond(_tsSeconds, _tsNanos.toLong()); + input.popLimit(_oldLimit); } + else { input.skipField(tag) } + } + return OrderUpdate(orderId, status, updatedAt) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/Outer.kt b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/Outer.kt new file mode 100644 index 0000000000..79e2c7fbba --- /dev/null +++ b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/Outer.kt @@ -0,0 +1,74 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +data class Outer( + val name: kotlin.String, + val inner: Inner? +) { + fun getSerializedSize(): Int { + var size: Int = 0 + size = size + CodedOutputStream.computeStringSize(1, this.name) + if ((this.inner != null)) { + size = size + CodedOutputStream.computeTagSize(2) + CodedOutputStream.computeUInt32SizeNoTag(this.inner.getSerializedSize()) + this.inner.getSerializedSize() + } + return size + } + + @Throws(IOException::class) + fun writeTo(output: CodedOutputStream) { + output.writeString(1, this.name) + if ((this.inner != null)) { + output.writeTag(2, 2); + output.writeUInt32NoTag(this.inner.getSerializedSize()); + this.inner.writeTo(output); + } + } + + companion object { + val MARSHALLER: Marshaller = + object : Marshaller { + override fun stream(value: Outer): InputStream { + val bytes = ByteArray(value.getSerializedSize()) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch (e: IOException) { + throw RuntimeException(e) + } + return ByteArrayInputStream(bytes) + } + override fun parse(stream: InputStream): Outer { + try { + return Outer.parseFrom(CodedInputStream.newInstance(stream)) + } catch (e: IOException) { + throw RuntimeException(e) + } + } + } + + @Throws(IOException::class) + fun parseFrom(input: CodedInputStream): Outer { + var name: kotlin.String = "" + var inner: Inner? = null + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { name = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 2) { val _length = input.readRawVarint32(); + val _oldLimit = input.pushLimit(_length); + inner = Inner.parseFrom(input); + input.popLimit(_oldLimit); } + else { input.skipField(tag) } + } + return Outer(name, inner) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/PaymentMethod.kt b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/PaymentMethod.kt new file mode 100644 index 0000000000..691ab3a966 --- /dev/null +++ b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/PaymentMethod.kt @@ -0,0 +1,104 @@ +package com.example.grpc + +import com.example.grpc.PaymentMethodMethod.BankTransferValue +import com.example.grpc.PaymentMethodMethod.CreditCardValue +import com.example.grpc.PaymentMethodMethod.WalletValue +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +data class PaymentMethod( + val id: kotlin.String, + val method: PaymentMethodMethod? +) { + fun getSerializedSize(): Int { + var size: Int = 0 + size = size + CodedOutputStream.computeStringSize(1, this.id) + when (val __r = this.method) { + null -> {} + is CreditCardValue -> { val c = __r; size = size + CodedOutputStream.computeTagSize(2) + CodedOutputStream.computeUInt32SizeNoTag(c.creditCard.getSerializedSize()) + c.creditCard.getSerializedSize() } + is BankTransferValue -> { val c = __r; size = size + CodedOutputStream.computeTagSize(3) + CodedOutputStream.computeUInt32SizeNoTag(c.bankTransfer.getSerializedSize()) + c.bankTransfer.getSerializedSize() } + is WalletValue -> { val c = __r; size = size + CodedOutputStream.computeTagSize(4) + CodedOutputStream.computeUInt32SizeNoTag(c.wallet.getSerializedSize()) + c.wallet.getSerializedSize() } + } + return size + } + + @Throws(IOException::class) + fun writeTo(output: CodedOutputStream) { + output.writeString(1, this.id) + when (val __r = this.method) { + null -> {} + is CreditCardValue -> { + val c = __r + output.writeTag(2, 2); + output.writeUInt32NoTag(c.creditCard.getSerializedSize()); + c.creditCard.writeTo(output); + } + is BankTransferValue -> { + val c = __r + output.writeTag(3, 2); + output.writeUInt32NoTag(c.bankTransfer.getSerializedSize()); + c.bankTransfer.writeTo(output); + } + is WalletValue -> { + val c = __r + output.writeTag(4, 2); + output.writeUInt32NoTag(c.wallet.getSerializedSize()); + c.wallet.writeTo(output); + } + } + } + + companion object { + val MARSHALLER: Marshaller = + object : Marshaller { + override fun stream(value: PaymentMethod): InputStream { + val bytes = ByteArray(value.getSerializedSize()) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch (e: IOException) { + throw RuntimeException(e) + } + return ByteArrayInputStream(bytes) + } + override fun parse(stream: InputStream): PaymentMethod { + try { + return PaymentMethod.parseFrom(CodedInputStream.newInstance(stream)) + } catch (e: IOException) { + throw RuntimeException(e) + } + } + } + + @Throws(IOException::class) + fun parseFrom(input: CodedInputStream): PaymentMethod { + var id: kotlin.String = "" + var method: PaymentMethodMethod? = null + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { id = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 2) { val _length = input.readRawVarint32(); + val _oldLimit = input.pushLimit(_length); + method = CreditCardValue(CreditCard.parseFrom(input)); + input.popLimit(_oldLimit); } + else if (WireFormat.getTagFieldNumber(tag) == 3) { val _length = input.readRawVarint32(); + val _oldLimit = input.pushLimit(_length); + method = BankTransferValue(BankTransfer.parseFrom(input)); + input.popLimit(_oldLimit); } + else if (WireFormat.getTagFieldNumber(tag) == 4) { val _length = input.readRawVarint32(); + val _oldLimit = input.pushLimit(_length); + method = WalletValue(Wallet.parseFrom(input)); + input.popLimit(_oldLimit); } + else { input.skipField(tag) } + } + return PaymentMethod(id, method) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/PaymentMethodMethod.kt b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/PaymentMethodMethod.kt new file mode 100644 index 0000000000..9850a8acd3 --- /dev/null +++ b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/PaymentMethodMethod.kt @@ -0,0 +1,12 @@ +package com.example.grpc + + + +/** OneOf type for method */ +sealed interface PaymentMethodMethod { + data class BankTransferValue(val bankTransfer: BankTransfer) : PaymentMethodMethod + + data class CreditCardValue(val creditCard: CreditCard) : PaymentMethodMethod + + data class WalletValue(val wallet: Wallet) : PaymentMethodMethod +} \ No newline at end of file diff --git a/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/Priority.kt b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/Priority.kt new file mode 100644 index 0000000000..d800dfda37 --- /dev/null +++ b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/Priority.kt @@ -0,0 +1,36 @@ +package com.example.grpc + +import java.lang.IllegalArgumentException + +enum class Priority(val value: kotlin.String) { + PRIORITY_UNSPECIFIED("PRIORITY_UNSPECIFIED"), + PRIORITY_LOW("PRIORITY_LOW"), + PRIORITY_MEDIUM("PRIORITY_MEDIUM"), + PRIORITY_HIGH("PRIORITY_HIGH"), + PRIORITY_CRITICAL("PRIORITY_CRITICAL"); + + fun toValue(): Int { + if (this.toString().equals("PRIORITY_UNSPECIFIED")) { return 0 } + else if (this.toString().equals("PRIORITY_LOW")) { return 1 } + else if (this.toString().equals("PRIORITY_MEDIUM")) { return 2 } + else if (this.toString().equals("PRIORITY_HIGH")) { return 3 } + else if (this.toString().equals("PRIORITY_CRITICAL")) { return 4 } + else { return 0 } + } + + companion object { + val Names: kotlin.String = entries.joinToString(", ") { it.value } + val ByName: kotlin.collections.Map = entries.associateBy { it.value } + fun fromValue(value: Int): Priority { + if (value == 0) { return Priority.PRIORITY_UNSPECIFIED } + else if (value == 1) { return Priority.PRIORITY_LOW } + else if (value == 2) { return Priority.PRIORITY_MEDIUM } + else if (value == 3) { return Priority.PRIORITY_HIGH } + else if (value == 4) { return Priority.PRIORITY_CRITICAL } + else { throw IllegalArgumentException("Unknown enum value: " + value) } + } + + fun force(str: kotlin.String): Priority = + ByName[str] ?: throw RuntimeException("'$str' does not match any of the following legal values: $Names") + } +} diff --git a/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/ScalarTypes.kt b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/ScalarTypes.kt new file mode 100644 index 0000000000..9b884d2baa --- /dev/null +++ b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/ScalarTypes.kt @@ -0,0 +1,131 @@ +package com.example.grpc + +import com.google.protobuf.ByteString +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +data class ScalarTypes( + val doubleVal: kotlin.Double, + val floatVal: kotlin.Float, + val int32Val: Int, + val int64Val: kotlin.Long, + val uint32Val: Int, + val uint64Val: kotlin.Long, + val sint32Val: Int, + val sint64Val: kotlin.Long, + val fixed32Val: Int, + val fixed64Val: kotlin.Long, + val sfixed32Val: Int, + val sfixed64Val: kotlin.Long, + val boolVal: kotlin.Boolean, + val stringVal: kotlin.String, + val bytesVal: ByteString +) { + fun getSerializedSize(): Int { + var size: Int = 0 + size = size + CodedOutputStream.computeDoubleSize(1, this.doubleVal) + size = size + CodedOutputStream.computeFloatSize(2, this.floatVal) + size = size + CodedOutputStream.computeInt32Size(3, this.int32Val) + size = size + CodedOutputStream.computeInt64Size(4, this.int64Val) + size = size + CodedOutputStream.computeUInt32Size(5, this.uint32Val) + size = size + CodedOutputStream.computeUInt64Size(6, this.uint64Val) + size = size + CodedOutputStream.computeSInt32Size(7, this.sint32Val) + size = size + CodedOutputStream.computeSInt64Size(8, this.sint64Val) + size = size + CodedOutputStream.computeFixed32Size(9, this.fixed32Val) + size = size + CodedOutputStream.computeFixed64Size(10, this.fixed64Val) + size = size + CodedOutputStream.computeSFixed32Size(11, this.sfixed32Val) + size = size + CodedOutputStream.computeSFixed64Size(12, this.sfixed64Val) + size = size + CodedOutputStream.computeBoolSize(13, this.boolVal) + size = size + CodedOutputStream.computeStringSize(14, this.stringVal) + size = size + CodedOutputStream.computeBytesSize(15, this.bytesVal) + return size + } + + @Throws(IOException::class) + fun writeTo(output: CodedOutputStream) { + output.writeDouble(1, this.doubleVal) + output.writeFloat(2, this.floatVal) + output.writeInt32(3, this.int32Val) + output.writeInt64(4, this.int64Val) + output.writeUInt32(5, this.uint32Val) + output.writeUInt64(6, this.uint64Val) + output.writeSInt32(7, this.sint32Val) + output.writeSInt64(8, this.sint64Val) + output.writeFixed32(9, this.fixed32Val) + output.writeFixed64(10, this.fixed64Val) + output.writeSFixed32(11, this.sfixed32Val) + output.writeSFixed64(12, this.sfixed64Val) + output.writeBool(13, this.boolVal) + output.writeString(14, this.stringVal) + output.writeBytes(15, this.bytesVal) + } + + companion object { + val MARSHALLER: Marshaller = + object : Marshaller { + override fun stream(value: ScalarTypes): InputStream { + val bytes = ByteArray(value.getSerializedSize()) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch (e: IOException) { + throw RuntimeException(e) + } + return ByteArrayInputStream(bytes) + } + override fun parse(stream: InputStream): ScalarTypes { + try { + return ScalarTypes.parseFrom(CodedInputStream.newInstance(stream)) + } catch (e: IOException) { + throw RuntimeException(e) + } + } + } + + @Throws(IOException::class) + fun parseFrom(input: CodedInputStream): ScalarTypes { + var doubleVal: kotlin.Double = 0.0 + var floatVal: kotlin.Float = 0.0f + var int32Val: Int = 0 + var int64Val: kotlin.Long = 0L + var uint32Val: Int = 0 + var uint64Val: kotlin.Long = 0L + var sint32Val: Int = 0 + var sint64Val: kotlin.Long = 0L + var fixed32Val: Int = 0 + var fixed64Val: kotlin.Long = 0L + var sfixed32Val: Int = 0 + var sfixed64Val: kotlin.Long = 0L + var boolVal: kotlin.Boolean = false + var stringVal: kotlin.String = "" + var bytesVal: ByteString = ByteString.EMPTY + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { doubleVal = input.readDouble() } + else if (WireFormat.getTagFieldNumber(tag) == 2) { floatVal = input.readFloat() } + else if (WireFormat.getTagFieldNumber(tag) == 3) { int32Val = input.readInt32() } + else if (WireFormat.getTagFieldNumber(tag) == 4) { int64Val = input.readInt64() } + else if (WireFormat.getTagFieldNumber(tag) == 5) { uint32Val = input.readUInt32() } + else if (WireFormat.getTagFieldNumber(tag) == 6) { uint64Val = input.readUInt64() } + else if (WireFormat.getTagFieldNumber(tag) == 7) { sint32Val = input.readSInt32() } + else if (WireFormat.getTagFieldNumber(tag) == 8) { sint64Val = input.readSInt64() } + else if (WireFormat.getTagFieldNumber(tag) == 9) { fixed32Val = input.readFixed32() } + else if (WireFormat.getTagFieldNumber(tag) == 10) { fixed64Val = input.readFixed64() } + else if (WireFormat.getTagFieldNumber(tag) == 11) { sfixed32Val = input.readSFixed32() } + else if (WireFormat.getTagFieldNumber(tag) == 12) { sfixed64Val = input.readSFixed64() } + else if (WireFormat.getTagFieldNumber(tag) == 13) { boolVal = input.readBool() } + else if (WireFormat.getTagFieldNumber(tag) == 14) { stringVal = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 15) { bytesVal = input.readBytes() } + else { input.skipField(tag) } + } + return ScalarTypes(doubleVal, floatVal, int32Val, int64Val, uint32Val, uint64Val, sint32Val, sint64Val, fixed32Val, fixed64Val, sfixed32Val, sfixed64Val, boolVal, stringVal, bytesVal) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/Wallet.kt b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/Wallet.kt new file mode 100644 index 0000000000..48942add65 --- /dev/null +++ b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/Wallet.kt @@ -0,0 +1,65 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +data class Wallet( + val walletId: kotlin.String, + val provider: kotlin.String +) { + fun getSerializedSize(): Int { + var size: Int = 0 + size = size + CodedOutputStream.computeStringSize(1, this.walletId) + size = size + CodedOutputStream.computeStringSize(2, this.provider) + return size + } + + @Throws(IOException::class) + fun writeTo(output: CodedOutputStream) { + output.writeString(1, this.walletId) + output.writeString(2, this.provider) + } + + companion object { + val MARSHALLER: Marshaller = + object : Marshaller { + override fun stream(value: Wallet): InputStream { + val bytes = ByteArray(value.getSerializedSize()) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch (e: IOException) { + throw RuntimeException(e) + } + return ByteArrayInputStream(bytes) + } + override fun parse(stream: InputStream): Wallet { + try { + return Wallet.parseFrom(CodedInputStream.newInstance(stream)) + } catch (e: IOException) { + throw RuntimeException(e) + } + } + } + + @Throws(IOException::class) + fun parseFrom(input: CodedInputStream): Wallet { + var walletId: kotlin.String = "" + var provider: kotlin.String = "" + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { walletId = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 2) { provider = input.readString() } + else { input.skipField(tag) } + } + return Wallet(walletId, provider) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/WellKnownTypesMessage.kt b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/WellKnownTypesMessage.kt new file mode 100644 index 0000000000..829678a1c0 --- /dev/null +++ b/testers/grpc/kotlin/generated-and-checked-in/com/example/grpc/WellKnownTypesMessage.kt @@ -0,0 +1,146 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException +import java.time.Duration +import java.time.Instant + +data class WellKnownTypesMessage( + val createdAt: Instant, + val ttl: Duration, + val nullableString: kotlin.String?, + val nullableInt: Int?, + val nullableBool: kotlin.Boolean? +) { + fun getSerializedSize(): Int { + var size: Int = 0 + size = size + CodedOutputStream.computeTagSize(1) + CodedOutputStream.computeUInt32SizeNoTag(CodedOutputStream.computeInt64Size(1, this.createdAt.getEpochSecond()) + CodedOutputStream.computeInt32Size(2, this.createdAt.getNano())) + CodedOutputStream.computeInt64Size(1, this.createdAt.getEpochSecond()) + CodedOutputStream.computeInt32Size(2, this.createdAt.getNano()) + size = size + CodedOutputStream.computeTagSize(2) + CodedOutputStream.computeUInt32SizeNoTag(CodedOutputStream.computeInt64Size(1, this.ttl.getSeconds()) + CodedOutputStream.computeInt32Size(2, this.ttl.getNano())) + CodedOutputStream.computeInt64Size(1, this.ttl.getSeconds()) + CodedOutputStream.computeInt32Size(2, this.ttl.getNano()) + if (this.nullableString != null) { + val v = this.nullableString!!; + size = size + CodedOutputStream.computeTagSize(3) + CodedOutputStream.computeUInt32SizeNoTag(CodedOutputStream.computeStringSize(1, v)) + CodedOutputStream.computeStringSize(1, v); + } + if (this.nullableInt != null) { + val v = this.nullableInt!!; + size = size + CodedOutputStream.computeTagSize(4) + CodedOutputStream.computeUInt32SizeNoTag(CodedOutputStream.computeInt32Size(1, v)) + CodedOutputStream.computeInt32Size(1, v); + } + if (this.nullableBool != null) { + val v = this.nullableBool!!; + size = size + CodedOutputStream.computeTagSize(5) + CodedOutputStream.computeUInt32SizeNoTag(CodedOutputStream.computeBoolSize(1, v)) + CodedOutputStream.computeBoolSize(1, v); + } + return size + } + + @Throws(IOException::class) + fun writeTo(output: CodedOutputStream) { + output.writeTag(1, 2) + output.writeUInt32NoTag(CodedOutputStream.computeInt64Size(1, this.createdAt.getEpochSecond()) + CodedOutputStream.computeInt32Size(2, this.createdAt.getNano())) + output.writeInt64(1, this.createdAt.getEpochSecond()) + output.writeInt32(2, this.createdAt.getNano()) + output.writeTag(2, 2) + output.writeUInt32NoTag(CodedOutputStream.computeInt64Size(1, this.ttl.getSeconds()) + CodedOutputStream.computeInt32Size(2, this.ttl.getNano())) + output.writeInt64(1, this.ttl.getSeconds()) + output.writeInt32(2, this.ttl.getNano()) + if (this.nullableString != null) { + val v = this.nullableString!!; + output.writeTag(3, 2); + output.writeUInt32NoTag(CodedOutputStream.computeStringSize(1, v)); + output.writeString(1, v); + } + if (this.nullableInt != null) { + val v = this.nullableInt!!; + output.writeTag(4, 2); + output.writeUInt32NoTag(CodedOutputStream.computeInt32Size(1, v)); + output.writeInt32(1, v); + } + if (this.nullableBool != null) { + val v = this.nullableBool!!; + output.writeTag(5, 2); + output.writeUInt32NoTag(CodedOutputStream.computeBoolSize(1, v)); + output.writeBool(1, v); + } + } + + companion object { + val MARSHALLER: Marshaller = + object : Marshaller { + override fun stream(value: WellKnownTypesMessage): InputStream { + val bytes = ByteArray(value.getSerializedSize()) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch (e: IOException) { + throw RuntimeException(e) + } + return ByteArrayInputStream(bytes) + } + override fun parse(stream: InputStream): WellKnownTypesMessage { + try { + return WellKnownTypesMessage.parseFrom(CodedInputStream.newInstance(stream)) + } catch (e: IOException) { + throw RuntimeException(e) + } + } + } + + @Throws(IOException::class) + fun parseFrom(input: CodedInputStream): WellKnownTypesMessage { + var createdAt: Instant = Instant.EPOCH + var ttl: Duration = Duration.ZERO + var nullableString: kotlin.String? = null + var nullableInt: Int? = null + var nullableBool: kotlin.Boolean? = null + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { val _length = input.readRawVarint32(); + val _oldLimit = input.pushLimit(_length); + var _tsSeconds = 0L; + var _tsNanos = 0; + while (!input.isAtEnd()) { + val _tsTag = input.readTag() + if (WireFormat.getTagFieldNumber(_tsTag) == 1) { _tsSeconds = input.readInt64() } + else if (WireFormat.getTagFieldNumber(_tsTag) == 2) { _tsNanos = input.readInt32() } + else { input.skipField(_tsTag) } + }; + createdAt = Instant.ofEpochSecond(_tsSeconds, _tsNanos.toLong()); + input.popLimit(_oldLimit); } + else if (WireFormat.getTagFieldNumber(tag) == 2) { val _length = input.readRawVarint32(); + val _oldLimit = input.pushLimit(_length); + var _durSeconds = 0L; + var _durNanos = 0; + while (!input.isAtEnd()) { + val _durTag = input.readTag() + if (WireFormat.getTagFieldNumber(_durTag) == 1) { _durSeconds = input.readInt64() } + else if (WireFormat.getTagFieldNumber(_durTag) == 2) { _durNanos = input.readInt32() } + else { input.skipField(_durTag) } + }; + ttl = Duration.ofSeconds(_durSeconds, _durNanos.toLong()); + input.popLimit(_oldLimit); } + else if (WireFormat.getTagFieldNumber(tag) == 3) { val _length = input.readRawVarint32(); + val _oldLimit = input.pushLimit(_length); + input.readTag(); + nullableString = input.readString(); + input.popLimit(_oldLimit); } + else if (WireFormat.getTagFieldNumber(tag) == 4) { val _length = input.readRawVarint32(); + val _oldLimit = input.pushLimit(_length); + input.readTag(); + nullableInt = input.readInt32(); + input.popLimit(_oldLimit); } + else if (WireFormat.getTagFieldNumber(tag) == 5) { val _length = input.readRawVarint32(); + val _oldLimit = input.pushLimit(_length); + input.readTag(); + nullableBool = input.readBool(); + input.popLimit(_oldLimit); } + else { input.skipField(tag) } + } + return WellKnownTypesMessage(createdAt, ttl, nullableString, nullableInt, nullableBool) + } + } +} \ No newline at end of file diff --git a/testers/grpc/kotlin/gradle.properties b/testers/grpc/kotlin/gradle.properties new file mode 100644 index 0000000000..14bdf6b2b0 --- /dev/null +++ b/testers/grpc/kotlin/gradle.properties @@ -0,0 +1 @@ +kotlin.daemon.jvmargs=-Xmx4g diff --git a/testers/grpc/kotlin/src/test/kotlin/com/example/grpc/GrpcIntegrationTest.kt b/testers/grpc/kotlin/src/test/kotlin/com/example/grpc/GrpcIntegrationTest.kt new file mode 100644 index 0000000000..922f5ca016 --- /dev/null +++ b/testers/grpc/kotlin/src/test/kotlin/com/example/grpc/GrpcIntegrationTest.kt @@ -0,0 +1,449 @@ +package com.example.grpc + +import com.google.protobuf.ByteString +import io.grpc.ManagedChannel +import io.grpc.Server +import io.grpc.inprocess.InProcessChannelBuilder +import io.grpc.inprocess.InProcessServerBuilder +import org.junit.After +import org.junit.Assert.* +import org.junit.Before +import org.junit.Test +import java.time.Duration +import java.time.Instant + +class GrpcIntegrationTest { + + private lateinit var server: Server + private lateinit var channel: ManagedChannel + private lateinit var orderClient: OrderServiceClient + private lateinit var echoClient: EchoServiceClient + + private val testCustomer = Customer(CustomerId.valueOf("CUST-123"), "John Doe", "john@example.com") + + @Before + fun setUp() { + val serverName = InProcessServerBuilder.generateName() + + val orderImpl = object : OrderService { + override fun getCustomer(request: GetCustomerRequest): GetCustomerResponse = + GetCustomerResponse(Customer(CustomerId.valueOf(request.customerId), "John Doe", "john@example.com")) + + override fun createOrder(request: CreateOrderRequest): CreateOrderResponse = + CreateOrderResponse(request.order!!.orderId.unwrap(), OrderStatus.ORDER_STATUS_PENDING) + + override fun listOrders(request: ListOrdersRequest): Iterator { + val updates = mutableListOf( + OrderUpdate("ORD-1", OrderStatus.ORDER_STATUS_PENDING, Instant.ofEpochSecond(1000, 500)), + OrderUpdate("ORD-2", OrderStatus.ORDER_STATUS_SHIPPED, Instant.ofEpochSecond(2000, 1000)), + OrderUpdate("ORD-3", OrderStatus.ORDER_STATUS_DELIVERED, Instant.ofEpochSecond(3000, 0)) + ) + return updates.iterator() + } + + override fun submitOrders(requests: Iterator): OrderSummary = + throw UnsupportedOperationException() + + override fun chat(requests: Iterator): Iterator = + throw UnsupportedOperationException() + } + + val echoImpl = object : EchoService { + override fun echoScalarTypes(request: ScalarTypes): ScalarTypes = request + override fun echoCustomer(request: Customer): Customer = request + override fun echoOrder(request: Order): Order = request + override fun echoInventory(request: Inventory): Inventory = request + override fun echoOuter(request: Outer): Outer = request + override fun echoOptionalFields(request: OptionalFields): OptionalFields = request + override fun echoWellKnownTypes(request: WellKnownTypesMessage): WellKnownTypesMessage = request + override fun echoPaymentMethod(request: PaymentMethod): PaymentMethod = request + override fun echoNotification(request: Notification): Notification = request + } + + server = InProcessServerBuilder + .forName(serverName) + .directExecutor() + .addService(OrderServiceServer(orderImpl)) + .addService(EchoServiceServer(echoImpl)) + .build() + .start() + + channel = InProcessChannelBuilder.forName(serverName).directExecutor().build() + orderClient = OrderServiceClient(channel) + echoClient = EchoServiceClient(channel) + } + + @After + fun tearDown() { + channel.shutdownNow() + server.shutdownNow() + } + + // ---- gRPC service tests ---- + + @Test + fun testGetCustomer() { + val response = orderClient.getCustomer(GetCustomerRequest("CUST-123")) + assertNotNull(response) + assertNotNull(response.customer) + assertEquals("CUST-123", response.customer!!.customerId.unwrap()) + assertEquals("John Doe", response.customer!!.name) + assertEquals("john@example.com", response.customer!!.email) + } + + @Test + fun testCreateOrder() { + val order = Order( + OrderId.valueOf("ORD-42"), + CustomerId.valueOf("CUST-1"), + 9999L, + Instant.ofEpochSecond(1700000000L, 123456789) + ) + + val response = orderClient.createOrder(CreateOrderRequest(order)) + assertNotNull(response) + assertEquals("ORD-42", response.orderId) + assertEquals(OrderStatus.ORDER_STATUS_PENDING, response.status) + } + + @Test + fun testListOrders() { + val updates = orderClient.listOrders(ListOrdersRequest("CUST-123", 10)) + val results = mutableListOf() + for (update in updates) { + results.add(update) + } + + assertEquals(3, results.size) + assertEquals("ORD-1", results[0].orderId) + assertEquals(OrderStatus.ORDER_STATUS_PENDING, results[0].status) + assertEquals("ORD-2", results[1].orderId) + assertEquals(OrderStatus.ORDER_STATUS_SHIPPED, results[1].status) + assertEquals("ORD-3", results[2].orderId) + assertEquals(OrderStatus.ORDER_STATUS_DELIVERED, results[2].status) + } + + // ---- Echo round-trip tests ---- + + @Test + fun testEchoCustomer() { + val parsed = echoClient.echoCustomer(testCustomer) + assertEquals(testCustomer, parsed) + } + + @Test + fun testEchoOrder() { + val order = Order( + OrderId.valueOf("ORD-1"), + CustomerId.valueOf("CUST-1"), + 5000L, + Instant.ofEpochSecond(1700000000L, 123456789) + ) + val parsed = echoClient.echoOrder(order) + assertEquals(order.orderId, parsed.orderId) + assertEquals(order.customerId, parsed.customerId) + assertEquals(order.amountCents, parsed.amountCents) + assertEquals(order.createdAt, parsed.createdAt) + } + + // ---- Scalar types ---- + + @Test + fun testEchoScalarTypes() { + val scalars = ScalarTypes( + 3.14, + 2.71f, + 42, + 9876543210L, + 100, + 200L, + -50, + -100L, + 999, + 888L, + -777, + -666L, + true, + "hello world", + ByteString.copyFromUtf8("binary data") + ) + val parsed = echoClient.echoScalarTypes(scalars) + assertEquals(scalars.doubleVal, parsed.doubleVal, 0.0001) + assertEquals(scalars.floatVal, parsed.floatVal, 0.0001f) + assertEquals(scalars.int32Val, parsed.int32Val) + assertEquals(scalars.int64Val, parsed.int64Val) + assertEquals(scalars.uint32Val, parsed.uint32Val) + assertEquals(scalars.uint64Val, parsed.uint64Val) + assertEquals(scalars.sint32Val, parsed.sint32Val) + assertEquals(scalars.sint64Val, parsed.sint64Val) + assertEquals(scalars.fixed32Val, parsed.fixed32Val) + assertEquals(scalars.fixed64Val, parsed.fixed64Val) + assertEquals(scalars.sfixed32Val, parsed.sfixed32Val) + assertEquals(scalars.sfixed64Val, parsed.sfixed64Val) + assertEquals(scalars.boolVal, parsed.boolVal) + assertEquals(scalars.stringVal, parsed.stringVal) + assertEquals(scalars.bytesVal, parsed.bytesVal) + } + + // ---- Enum tests ---- + + @Test + fun testEnumToValueFromValueRoundTrip() { + for (status in OrderStatus.entries) { + val wireValue = status.toValue() + val back = OrderStatus.fromValue(wireValue) + assertEquals(status, back) + } + } + + @Test + fun testEnumForce() { + assertEquals(OrderStatus.ORDER_STATUS_PENDING, OrderStatus.force("ORDER_STATUS_PENDING")) + } + + @Test(expected = RuntimeException::class) + fun testEnumForceInvalid() { + OrderStatus.force("NONEXISTENT") + } + + @Test(expected = IllegalArgumentException::class) + fun testEnumFromValueInvalid() { + OrderStatus.fromValue(999) + } + + @Test + fun testPriorityEnumRoundTrip() { + for (p in Priority.entries) { + val wireValue = p.toValue() + val back = Priority.fromValue(wireValue) + assertEquals(p, back) + } + } + + // ---- Optional fields ---- + + @Test + fun testEchoOptionalFieldsAllPresent() { + val opt = OptionalFields("Alice", 30, testCustomer) + val parsed = echoClient.echoOptionalFields(opt) + assertEquals("Alice", parsed.name) + assertEquals(30, parsed.age) + assertNotNull(parsed.customer) + assertEquals("CUST-123", parsed.customer!!.customerId.unwrap()) + } + + @Test + fun testEchoOptionalFieldsAllEmpty() { + val opt = OptionalFields(null, null, null) + val parsed = echoClient.echoOptionalFields(opt) + assertNull(parsed.name) + assertNull(parsed.age) + assertNull(parsed.customer) + } + + @Test + fun testEchoOptionalFieldsPartiallyPresent() { + val opt = OptionalFields("Bob", null, null) + val parsed = echoClient.echoOptionalFields(opt) + assertEquals("Bob", parsed.name) + assertNull(parsed.age) + assertNull(parsed.customer) + } + + // ---- Nested messages ---- + + @Test + fun testEchoOuter() { + val outer = Outer("outer-name", Inner(42, "inner-desc")) + val parsed = echoClient.echoOuter(outer) + assertEquals("outer-name", parsed.name) + assertNotNull(parsed.inner) + assertEquals(42, parsed.inner!!.value) + assertEquals("inner-desc", parsed.inner!!.description) + } + + // ---- OneOf types ---- + + @Test + fun testEchoPaymentMethodCreditCard() { + val cc = CreditCard("4111111111111111", "12/25", "123") + val method: PaymentMethodMethod = PaymentMethodMethod.CreditCardValue(cc) + val pm = PaymentMethod("PAY-1", method) + val parsed = echoClient.echoPaymentMethod(pm) + assertEquals("PAY-1", parsed.id) + assertNotNull(parsed.method) + assertTrue(parsed.method is PaymentMethodMethod.CreditCardValue) + val ccv = parsed.method as PaymentMethodMethod.CreditCardValue + assertEquals("4111111111111111", ccv.creditCard.cardNumber) + assertEquals("12/25", ccv.creditCard.expiryDate) + assertEquals("123", ccv.creditCard.cvv) + } + + @Test + fun testEchoPaymentMethodBankTransfer() { + val bt = BankTransfer("123456789", "021000021") + val method: PaymentMethodMethod = PaymentMethodMethod.BankTransferValue(bt) + val pm = PaymentMethod("PAY-2", method) + val parsed = echoClient.echoPaymentMethod(pm) + assertEquals("PAY-2", parsed.id) + assertNotNull(parsed.method) + assertTrue(parsed.method is PaymentMethodMethod.BankTransferValue) + val btv = parsed.method as PaymentMethodMethod.BankTransferValue + assertEquals("123456789", btv.bankTransfer.accountNumber) + assertEquals("021000021", btv.bankTransfer.routingNumber) + } + + @Test + fun testEchoPaymentMethodWallet() { + val w = Wallet("wallet-42", "Stripe") + val method: PaymentMethodMethod = PaymentMethodMethod.WalletValue(w) + val pm = PaymentMethod("PAY-3", method) + val parsed = echoClient.echoPaymentMethod(pm) + assertEquals("PAY-3", parsed.id) + assertNotNull(parsed.method) + assertTrue(parsed.method is PaymentMethodMethod.WalletValue) + val wv = parsed.method as PaymentMethodMethod.WalletValue + assertEquals("wallet-42", wv.wallet.walletId) + assertEquals("Stripe", wv.wallet.provider) + } + + @Test + fun testEchoNotificationWithEmailTarget() { + val notif = Notification("Hello!", Priority.PRIORITY_HIGH, NotificationTarget.Email("user@example.com")) + val parsed = echoClient.echoNotification(notif) + assertEquals("Hello!", parsed.message) + assertEquals(Priority.PRIORITY_HIGH, parsed.priority) + assertNotNull(parsed.target) + assertTrue(parsed.target is NotificationTarget.Email) + assertEquals("user@example.com", (parsed.target as NotificationTarget.Email).email) + } + + @Test + fun testEchoNotificationWithPhoneTarget() { + val notif = Notification("Alert", Priority.PRIORITY_CRITICAL, NotificationTarget.Phone("+1234567890")) + val parsed = echoClient.echoNotification(notif) + assertEquals("Alert", parsed.message) + assertEquals(Priority.PRIORITY_CRITICAL, parsed.priority) + assertNotNull(parsed.target) + assertTrue(parsed.target is NotificationTarget.Phone) + assertEquals("+1234567890", (parsed.target as NotificationTarget.Phone).phone) + } + + @Test + fun testEchoNotificationWithWebhookTarget() { + val notif = + Notification("Event", Priority.PRIORITY_LOW, NotificationTarget.WebhookUrl("https://hooks.example.com/abc")) + val parsed = echoClient.echoNotification(notif) + assertEquals("Event", parsed.message) + assertEquals(Priority.PRIORITY_LOW, parsed.priority) + assertNotNull(parsed.target) + assertTrue(parsed.target is NotificationTarget.WebhookUrl) + assertEquals("https://hooks.example.com/abc", (parsed.target as NotificationTarget.WebhookUrl).webhookUrl) + } + + // ---- Collections ---- + + @Test + fun testEchoInventory() { + val productIds = listOf("PROD-1", "PROD-2", "PROD-3") + val stockCounts = mapOf("PROD-1" to 100, "PROD-2" to 200, "PROD-3" to 0) + val orders = listOf( + Order(OrderId.valueOf("ORD-1"), CustomerId.valueOf("CUST-1"), 1000L, Instant.ofEpochSecond(1000, 0)), + Order(OrderId.valueOf("ORD-2"), CustomerId.valueOf("CUST-2"), 2000L, Instant.ofEpochSecond(2000, 0)) + ) + + val inventory = Inventory("WH-1", productIds, stockCounts, orders) + val parsed = echoClient.echoInventory(inventory) + assertEquals("WH-1", parsed.warehouseId) + assertEquals(3, parsed.productIds.size) + assertEquals("PROD-1", parsed.productIds[0]) + assertEquals("PROD-2", parsed.productIds[1]) + assertEquals("PROD-3", parsed.productIds[2]) + assertEquals(3, parsed.stockCounts.size) + assertEquals(100, parsed.stockCounts["PROD-1"]) + assertEquals(200, parsed.stockCounts["PROD-2"]) + assertEquals(0, parsed.stockCounts["PROD-3"]) + assertEquals(2, parsed.recentOrders.size) + assertEquals("ORD-1", parsed.recentOrders[0].orderId.unwrap()) + assertEquals("ORD-2", parsed.recentOrders[1].orderId.unwrap()) + } + + @Test + fun testEchoInventoryEmptyCollections() { + val inventory = Inventory("WH-EMPTY", emptyList(), emptyMap(), emptyList()) + val parsed = echoClient.echoInventory(inventory) + assertEquals("WH-EMPTY", parsed.warehouseId) + assertTrue(parsed.productIds.isEmpty()) + assertTrue(parsed.stockCounts.isEmpty()) + assertTrue(parsed.recentOrders.isEmpty()) + } + + // ---- Well-known types ---- + + @Test + fun testEchoWellKnownTypes() { + val msg = WellKnownTypesMessage( + Instant.ofEpochSecond(1700000000L, 123456789), + Duration.ofSeconds(3600, 500000000), + "hello", + 42, + true + ) + val parsed = echoClient.echoWellKnownTypes(msg) + assertEquals(msg.createdAt, parsed.createdAt) + assertEquals(msg.ttl, parsed.ttl) + assertEquals("hello", parsed.nullableString) + assertEquals(42, parsed.nullableInt) + assertEquals(true, parsed.nullableBool) + } + + // ---- Wrapper ID types ---- + + @Test + fun testCustomerIdValueOf() { + val id = CustomerId.valueOf("abc") + assertEquals("abc", id.unwrap()) + assertEquals("abc", id.toString()) + } + + @Test + fun testOrderIdValueOf() { + val id = OrderId.valueOf("ORD-1") + assertEquals("ORD-1", id.unwrap()) + assertEquals("ORD-1", id.toString()) + } + + // ---- With methods (copy) ---- + + @Test + fun testCustomerWithMethods() { + val updated = testCustomer.copy(name = "Jane Doe") + assertEquals("Jane Doe", updated.name) + assertEquals(testCustomer.customerId, updated.customerId) + assertEquals(testCustomer.email, updated.email) + } + + @Test + fun testOrderWithMethods() { + val order = Order( + OrderId.valueOf("ORD-1"), + CustomerId.valueOf("CUST-1"), + 1000L, + Instant.ofEpochSecond(1000, 0) + ) + val updated = order.copy(amountCents = 2000L) + assertEquals(2000L, updated.amountCents) + assertEquals(order.orderId, updated.orderId) + } + + // ---- Echo with empty strings ---- + + @Test + fun testEchoCustomerEmptyStrings() { + val empty = Customer(CustomerId.valueOf(""), "", "") + val parsed = echoClient.echoCustomer(empty) + assertEquals("", parsed.customerId.unwrap()) + assertEquals("", parsed.name) + assertEquals("", parsed.email) + } +} diff --git a/testers/grpc/protos/enums.proto b/testers/grpc/protos/enums.proto new file mode 100644 index 0000000000..a38a93f263 --- /dev/null +++ b/testers/grpc/protos/enums.proto @@ -0,0 +1,22 @@ +syntax = "proto3"; +package testgrpc; + +option java_package = "com.example.grpc.proto"; +option java_multiple_files = true; + +enum OrderStatus { + ORDER_STATUS_UNSPECIFIED = 0; + ORDER_STATUS_PENDING = 1; + ORDER_STATUS_PROCESSING = 2; + ORDER_STATUS_SHIPPED = 3; + ORDER_STATUS_DELIVERED = 4; + ORDER_STATUS_CANCELLED = 5; +} + +enum Priority { + PRIORITY_UNSPECIFIED = 0; + PRIORITY_LOW = 1; + PRIORITY_MEDIUM = 2; + PRIORITY_HIGH = 3; + PRIORITY_CRITICAL = 4; +} diff --git a/testers/grpc/protos/messages.proto b/testers/grpc/protos/messages.proto new file mode 100644 index 0000000000..8005db8030 --- /dev/null +++ b/testers/grpc/protos/messages.proto @@ -0,0 +1,78 @@ +syntax = "proto3"; +package testgrpc; + +import "typr/annotations.proto"; +import "google/protobuf/timestamp.proto"; +import "google/protobuf/duration.proto"; +import "google/protobuf/wrappers.proto"; + +option java_package = "com.example.grpc.proto"; +option java_multiple_files = true; + +// Simple message with all scalar types +message ScalarTypes { + double double_val = 1; + float float_val = 2; + int32 int32_val = 3; + int64 int64_val = 4; + uint32 uint32_val = 5; + uint64 uint64_val = 6; + sint32 sint32_val = 7; + sint64 sint64_val = 8; + fixed32 fixed32_val = 9; + fixed64 fixed64_val = 10; + sfixed32 sfixed32_val = 11; + sfixed64 sfixed64_val = 12; + bool bool_val = 13; + string string_val = 14; + bytes bytes_val = 15; +} + +// Message with wrapper types +message Customer { + string customer_id = 1 [(typr.wrapper) = "CustomerId"]; + string name = 2; + string email = 3; +} + +message Order { + string order_id = 1 [(typr.wrapper) = "OrderId"]; + string customer_id = 2 [(typr.wrapper) = "CustomerId"]; + int64 amount_cents = 3; + google.protobuf.Timestamp created_at = 4; +} + +// Message with repeated and map fields +message Inventory { + string warehouse_id = 1; + repeated string product_ids = 2; + map stock_counts = 3; + repeated Order recent_orders = 4; +} + +// Nested message +message Outer { + string name = 1; + Inner inner = 2; + + message Inner { + int32 value = 1; + string description = 2; + } +} + +// Message with optional fields (proto3 explicit optional) +message OptionalFields { + optional string name = 1; + optional int32 age = 2; + optional Customer customer = 3; +} + +// Well-known types +message WellKnownTypesMessage { + google.protobuf.Timestamp created_at = 1; + google.protobuf.Duration ttl = 2; + google.protobuf.StringValue nullable_string = 3; + google.protobuf.Int32Value nullable_int = 4; + google.protobuf.BoolValue nullable_bool = 5; +} diff --git a/testers/grpc/protos/oneofs.proto b/testers/grpc/protos/oneofs.proto new file mode 100644 index 0000000000..aea2b27bf1 --- /dev/null +++ b/testers/grpc/protos/oneofs.proto @@ -0,0 +1,46 @@ +syntax = "proto3"; +package testgrpc; + +import "enums.proto"; + +option java_package = "com.example.grpc.proto"; +option java_multiple_files = true; + +// Message with oneof +message PaymentMethod { + string id = 1; + + oneof method { + CreditCard credit_card = 2; + BankTransfer bank_transfer = 3; + Wallet wallet = 4; + } +} + +message CreditCard { + string card_number = 1; + string expiry_date = 2; + string cvv = 3; +} + +message BankTransfer { + string account_number = 1; + string routing_number = 2; +} + +message Wallet { + string wallet_id = 1; + string provider = 2; +} + +// Message with oneof and enum +message Notification { + string message = 1; + Priority priority = 2; + + oneof target { + string email = 3; + string phone = 4; + string webhook_url = 5; + } +} diff --git a/testers/grpc/protos/services.proto b/testers/grpc/protos/services.proto new file mode 100644 index 0000000000..312400bede --- /dev/null +++ b/testers/grpc/protos/services.proto @@ -0,0 +1,81 @@ +syntax = "proto3"; +package testgrpc; + +import "messages.proto"; +import "enums.proto"; +import "oneofs.proto"; +import "google/protobuf/timestamp.proto"; + +option java_package = "com.example.grpc.proto"; +option java_multiple_files = true; + +// Request/response messages for services +message GetCustomerRequest { + string customer_id = 1; +} + +message GetCustomerResponse { + Customer customer = 1; +} + +message CreateOrderRequest { + Order order = 1; +} + +message CreateOrderResponse { + string order_id = 1; + OrderStatus status = 2; +} + +message ListOrdersRequest { + string customer_id = 1; + int32 page_size = 2; +} + +message OrderUpdate { + string order_id = 1; + OrderStatus status = 2; + google.protobuf.Timestamp updated_at = 3; +} + +message ChatMessage { + string sender = 1; + string content = 2; + google.protobuf.Timestamp sent_at = 3; +} + +message OrderSummary { + int32 total_orders = 1; + int64 total_amount_cents = 2; +} + +// Service with all 4 RPC patterns +service OrderService { + // Unary + rpc GetCustomer(GetCustomerRequest) returns (GetCustomerResponse); + + // Unary + rpc CreateOrder(CreateOrderRequest) returns (CreateOrderResponse); + + // Server streaming + rpc ListOrders(ListOrdersRequest) returns (stream OrderUpdate); + + // Client streaming + rpc SubmitOrders(stream CreateOrderRequest) returns (OrderSummary); + + // Bidirectional streaming + rpc Chat(stream ChatMessage) returns (stream ChatMessage); +} + +// Echo service for roundtrip testing of all message types +service EchoService { + rpc EchoScalarTypes(ScalarTypes) returns (ScalarTypes); + rpc EchoCustomer(Customer) returns (Customer); + rpc EchoOrder(Order) returns (Order); + rpc EchoInventory(Inventory) returns (Inventory); + rpc EchoOuter(Outer) returns (Outer); + rpc EchoOptionalFields(OptionalFields) returns (OptionalFields); + rpc EchoWellKnownTypes(WellKnownTypesMessage) returns (WellKnownTypesMessage); + rpc EchoPaymentMethod(PaymentMethod) returns (PaymentMethod); + rpc EchoNotification(Notification) returns (Notification); +} diff --git a/testers/grpc/protos/typr/annotations.proto b/testers/grpc/protos/typr/annotations.proto new file mode 100644 index 0000000000..fd010c0693 --- /dev/null +++ b/testers/grpc/protos/typr/annotations.proto @@ -0,0 +1,8 @@ +syntax = "proto3"; +package typr; + +import "google/protobuf/descriptor.proto"; + +extend google.protobuf.FieldOptions { + optional string wrapper = 50000; +} diff --git a/testers/grpc/scala/generated-and-checked-in/com/example/grpc/BankTransfer.scala b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/BankTransfer.scala new file mode 100644 index 0000000000..8c9688ca38 --- /dev/null +++ b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/BankTransfer.scala @@ -0,0 +1,66 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +case class BankTransfer( + accountNumber: String, + routingNumber: String +) { + @throws[IOException] + def writeTo(output: CodedOutputStream): Unit = { + output.writeString(1, this.accountNumber) + output.writeString(2, this.routingNumber) + } + + def getSerializedSize: Int = { + var size: Int = 0 + size = size + CodedOutputStream.computeStringSize(1, this.accountNumber) + size = size + CodedOutputStream.computeStringSize(2, this.routingNumber) + return size + } +} + +object BankTransfer { + given marshaller: Marshaller[BankTransfer] = { + new Marshaller[BankTransfer] { + override def stream(value: BankTransfer): InputStream = { + val bytes = Array.ofDim[Byte](value.getSerializedSize) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch { + case e: IOException => throw new RuntimeException(e) + } + return new ByteArrayInputStream(bytes) + } + override def parse(stream: InputStream): BankTransfer = { + try { + return BankTransfer.parseFrom(CodedInputStream.newInstance(stream)) + } catch { + case e: IOException => throw new RuntimeException(e) + } + } + } + } + + @throws[IOException] + def parseFrom(input: CodedInputStream): BankTransfer = { + var accountNumber: String = "" + var routingNumber: String = "" + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { accountNumber = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 2) { routingNumber = input.readString() } + else { input.skipField(tag) } + } + return new BankTransfer(accountNumber, routingNumber) + } +} \ No newline at end of file diff --git a/testers/grpc/scala/generated-and-checked-in/com/example/grpc/ChatMessage.scala b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/ChatMessage.scala new file mode 100644 index 0000000000..42f1b7e8c8 --- /dev/null +++ b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/ChatMessage.scala @@ -0,0 +1,86 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException +import java.time.Instant + +case class ChatMessage( + sender: String, + content: String, + sentAt: Instant +) { + @throws[IOException] + def writeTo(output: CodedOutputStream): Unit = { + output.writeString(1, this.sender) + output.writeString(2, this.content) + output.writeTag(3, 2) + output.writeUInt32NoTag(CodedOutputStream.computeInt64Size(1, this.sentAt.getEpochSecond()) + CodedOutputStream.computeInt32Size(2, this.sentAt.getNano())) + output.writeInt64(1, this.sentAt.getEpochSecond()) + output.writeInt32(2, this.sentAt.getNano()) + } + + def getSerializedSize: Int = { + var size: Int = 0 + size = size + CodedOutputStream.computeStringSize(1, this.sender) + size = size + CodedOutputStream.computeStringSize(2, this.content) + size = size + CodedOutputStream.computeTagSize(3) + CodedOutputStream.computeUInt32SizeNoTag(CodedOutputStream.computeInt64Size(1, this.sentAt.getEpochSecond()) + CodedOutputStream.computeInt32Size(2, this.sentAt.getNano())) + CodedOutputStream.computeInt64Size(1, this.sentAt.getEpochSecond()) + CodedOutputStream.computeInt32Size(2, this.sentAt.getNano()) + return size + } +} + +object ChatMessage { + given marshaller: Marshaller[ChatMessage] = { + new Marshaller[ChatMessage] { + override def stream(value: ChatMessage): InputStream = { + val bytes = Array.ofDim[Byte](value.getSerializedSize) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch { + case e: IOException => throw new RuntimeException(e) + } + return new ByteArrayInputStream(bytes) + } + override def parse(stream: InputStream): ChatMessage = { + try { + return ChatMessage.parseFrom(CodedInputStream.newInstance(stream)) + } catch { + case e: IOException => throw new RuntimeException(e) + } + } + } + } + + @throws[IOException] + def parseFrom(input: CodedInputStream): ChatMessage = { + var sender: String = "" + var content: String = "" + var sentAt: Instant = Instant.EPOCH + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { sender = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 2) { content = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 3) { val `_length` = input.readRawVarint32(); + val `_oldLimit` = input.pushLimit(`_length`); + var `_tsSeconds` = 0L; + var `_tsNanos` = 0; + while (!input.isAtEnd()) { + val `_tsTag` = input.readTag() + if (WireFormat.getTagFieldNumber(`_tsTag`) == 1) { `_tsSeconds` = input.readInt64() } + else if (WireFormat.getTagFieldNumber(`_tsTag`) == 2) { `_tsNanos` = input.readInt32() } + else { input.skipField(`_tsTag`) } + }; + sentAt = Instant.ofEpochSecond(`_tsSeconds`, `_tsNanos`.toLong); + input.popLimit(`_oldLimit`); } + else { input.skipField(tag) } + } + return new ChatMessage(sender, content, sentAt) + } +} \ No newline at end of file diff --git a/testers/grpc/scala/generated-and-checked-in/com/example/grpc/CreateOrderRequest.scala b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/CreateOrderRequest.scala new file mode 100644 index 0000000000..b1409a7644 --- /dev/null +++ b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/CreateOrderRequest.scala @@ -0,0 +1,68 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +case class CreateOrderRequest(order: Order) { + @throws[IOException] + def writeTo(output: CodedOutputStream): Unit = { + if ((this.order != null)) { + output.writeTag(1, 2); + output.writeUInt32NoTag(this.order.getSerializedSize); + this.order.writeTo(output); + } + } + + def getSerializedSize: Int = { + var size: Int = 0 + if ((this.order != null)) { + size = size + CodedOutputStream.computeTagSize(1) + CodedOutputStream.computeUInt32SizeNoTag(this.order.getSerializedSize) + this.order.getSerializedSize + } + return size + } +} + +object CreateOrderRequest { + given marshaller: Marshaller[CreateOrderRequest] = { + new Marshaller[CreateOrderRequest] { + override def stream(value: CreateOrderRequest): InputStream = { + val bytes = Array.ofDim[Byte](value.getSerializedSize) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch { + case e: IOException => throw new RuntimeException(e) + } + return new ByteArrayInputStream(bytes) + } + override def parse(stream: InputStream): CreateOrderRequest = { + try { + return CreateOrderRequest.parseFrom(CodedInputStream.newInstance(stream)) + } catch { + case e: IOException => throw new RuntimeException(e) + } + } + } + } + + @throws[IOException] + def parseFrom(input: CodedInputStream): CreateOrderRequest = { + var order: Order = null + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { val `_length` = input.readRawVarint32(); + val `_oldLimit` = input.pushLimit(`_length`); + order = Order.parseFrom(input); + input.popLimit(`_oldLimit`); } + else { input.skipField(tag) } + } + return new CreateOrderRequest(order) + } +} \ No newline at end of file diff --git a/testers/grpc/scala/generated-and-checked-in/com/example/grpc/CreateOrderResponse.scala b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/CreateOrderResponse.scala new file mode 100644 index 0000000000..42d1856663 --- /dev/null +++ b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/CreateOrderResponse.scala @@ -0,0 +1,66 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +case class CreateOrderResponse( + orderId: String, + status: OrderStatus +) { + @throws[IOException] + def writeTo(output: CodedOutputStream): Unit = { + output.writeString(1, this.orderId) + output.writeEnum(2, this.status.toValue) + } + + def getSerializedSize: Int = { + var size: Int = 0 + size = size + CodedOutputStream.computeStringSize(1, this.orderId) + size = size + CodedOutputStream.computeEnumSize(2, this.status.toValue) + return size + } +} + +object CreateOrderResponse { + given marshaller: Marshaller[CreateOrderResponse] = { + new Marshaller[CreateOrderResponse] { + override def stream(value: CreateOrderResponse): InputStream = { + val bytes = Array.ofDim[Byte](value.getSerializedSize) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch { + case e: IOException => throw new RuntimeException(e) + } + return new ByteArrayInputStream(bytes) + } + override def parse(stream: InputStream): CreateOrderResponse = { + try { + return CreateOrderResponse.parseFrom(CodedInputStream.newInstance(stream)) + } catch { + case e: IOException => throw new RuntimeException(e) + } + } + } + } + + @throws[IOException] + def parseFrom(input: CodedInputStream): CreateOrderResponse = { + var orderId: String = "" + var status: OrderStatus = OrderStatus.fromValue(0) + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { orderId = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 2) { status = OrderStatus.fromValue(input.readEnum()) } + else { input.skipField(tag) } + } + return new CreateOrderResponse(orderId, status) + } +} \ No newline at end of file diff --git a/testers/grpc/scala/generated-and-checked-in/com/example/grpc/CreditCard.scala b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/CreditCard.scala new file mode 100644 index 0000000000..f2a23e2a51 --- /dev/null +++ b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/CreditCard.scala @@ -0,0 +1,71 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +case class CreditCard( + cardNumber: String, + expiryDate: String, + cvv: String +) { + @throws[IOException] + def writeTo(output: CodedOutputStream): Unit = { + output.writeString(1, this.cardNumber) + output.writeString(2, this.expiryDate) + output.writeString(3, this.cvv) + } + + def getSerializedSize: Int = { + var size: Int = 0 + size = size + CodedOutputStream.computeStringSize(1, this.cardNumber) + size = size + CodedOutputStream.computeStringSize(2, this.expiryDate) + size = size + CodedOutputStream.computeStringSize(3, this.cvv) + return size + } +} + +object CreditCard { + given marshaller: Marshaller[CreditCard] = { + new Marshaller[CreditCard] { + override def stream(value: CreditCard): InputStream = { + val bytes = Array.ofDim[Byte](value.getSerializedSize) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch { + case e: IOException => throw new RuntimeException(e) + } + return new ByteArrayInputStream(bytes) + } + override def parse(stream: InputStream): CreditCard = { + try { + return CreditCard.parseFrom(CodedInputStream.newInstance(stream)) + } catch { + case e: IOException => throw new RuntimeException(e) + } + } + } + } + + @throws[IOException] + def parseFrom(input: CodedInputStream): CreditCard = { + var cardNumber: String = "" + var expiryDate: String = "" + var cvv: String = "" + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { cardNumber = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 2) { expiryDate = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 3) { cvv = input.readString() } + else { input.skipField(tag) } + } + return new CreditCard(cardNumber, expiryDate, cvv) + } +} \ No newline at end of file diff --git a/testers/grpc/scala/generated-and-checked-in/com/example/grpc/Customer.scala b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/Customer.scala new file mode 100644 index 0000000000..26c6ce1cb8 --- /dev/null +++ b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/Customer.scala @@ -0,0 +1,71 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +case class Customer( + customerId: CustomerId, + name: String, + email: String +) { + @throws[IOException] + def writeTo(output: CodedOutputStream): Unit = { + output.writeString(1, this.customerId.unwrap) + output.writeString(2, this.name) + output.writeString(3, this.email) + } + + def getSerializedSize: Int = { + var size: Int = 0 + size = size + CodedOutputStream.computeStringSize(1, this.customerId.unwrap) + size = size + CodedOutputStream.computeStringSize(2, this.name) + size = size + CodedOutputStream.computeStringSize(3, this.email) + return size + } +} + +object Customer { + given marshaller: Marshaller[Customer] = { + new Marshaller[Customer] { + override def stream(value: Customer): InputStream = { + val bytes = Array.ofDim[Byte](value.getSerializedSize) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch { + case e: IOException => throw new RuntimeException(e) + } + return new ByteArrayInputStream(bytes) + } + override def parse(stream: InputStream): Customer = { + try { + return Customer.parseFrom(CodedInputStream.newInstance(stream)) + } catch { + case e: IOException => throw new RuntimeException(e) + } + } + } + } + + @throws[IOException] + def parseFrom(input: CodedInputStream): Customer = { + var customerId: CustomerId = CustomerId.valueOf("") + var name: String = "" + var email: String = "" + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { customerId = CustomerId.valueOf(input.readString()) } + else if (WireFormat.getTagFieldNumber(tag) == 2) { name = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 3) { email = input.readString() } + else { input.skipField(tag) } + } + return new Customer(customerId, name, email) + } +} \ No newline at end of file diff --git a/testers/grpc/scala/generated-and-checked-in/com/example/grpc/CustomerId.scala b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/CustomerId.scala new file mode 100644 index 0000000000..83e4d61446 --- /dev/null +++ b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/CustomerId.scala @@ -0,0 +1,18 @@ +package com.example.grpc + + + +/** Wrapper type for typr.grpc.GrpcCodegen$$$Lambda/0x00007fc001118400@6c4906d3 */ +case class CustomerId(value: String) extends scala.AnyVal { + /** Get the underlying value */ + def unwrap: String = { + return this.value + } +} + +object CustomerId { + /** Create a CustomerId from a raw value */ + def valueOf(v: String): CustomerId = { + return new CustomerId(v) + } +} \ No newline at end of file diff --git a/testers/grpc/scala/generated-and-checked-in/com/example/grpc/EchoService.scala b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/EchoService.scala new file mode 100644 index 0000000000..caad95a82b --- /dev/null +++ b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/EchoService.scala @@ -0,0 +1,24 @@ +package com.example.grpc + + + +/** Clean service interface for EchoService gRPC service */ +trait EchoService { + def echoScalarTypes(request: ScalarTypes): ScalarTypes + + def echoCustomer(request: Customer): Customer + + def echoOrder(request: Order): Order + + def echoInventory(request: Inventory): Inventory + + def echoOuter(request: Outer): Outer + + def echoOptionalFields(request: OptionalFields): OptionalFields + + def echoWellKnownTypes(request: WellKnownTypesMessage): WellKnownTypesMessage + + def echoPaymentMethod(request: PaymentMethod): PaymentMethod + + def echoNotification(request: Notification): Notification +} \ No newline at end of file diff --git a/testers/grpc/scala/generated-and-checked-in/com/example/grpc/EchoServiceClient.scala b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/EchoServiceClient.scala new file mode 100644 index 0000000000..7d1006968e --- /dev/null +++ b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/EchoServiceClient.scala @@ -0,0 +1,66 @@ +package com.example.grpc + +import io.grpc.CallOptions +import io.grpc.Channel +import io.grpc.MethodDescriptor +import io.grpc.MethodDescriptor.MethodType +import io.grpc.stub.ClientCalls + +/** gRPC client wrapper for EchoService - wraps Channel with clean types */ +class EchoServiceClient(val channel: Channel) extends EchoService { + override def echoScalarTypes(request: ScalarTypes): ScalarTypes = { + return ClientCalls.blockingUnaryCall(channel, EchoServiceClient.ECHO_SCALAR_TYPES, CallOptions.DEFAULT, request) + } + + override def echoCustomer(request: Customer): Customer = { + return ClientCalls.blockingUnaryCall(channel, EchoServiceClient.ECHO_CUSTOMER, CallOptions.DEFAULT, request) + } + + override def echoOrder(request: Order): Order = { + return ClientCalls.blockingUnaryCall(channel, EchoServiceClient.ECHO_ORDER, CallOptions.DEFAULT, request) + } + + override def echoInventory(request: Inventory): Inventory = { + return ClientCalls.blockingUnaryCall(channel, EchoServiceClient.ECHO_INVENTORY, CallOptions.DEFAULT, request) + } + + override def echoOuter(request: Outer): Outer = { + return ClientCalls.blockingUnaryCall(channel, EchoServiceClient.ECHO_OUTER, CallOptions.DEFAULT, request) + } + + override def echoOptionalFields(request: OptionalFields): OptionalFields = { + return ClientCalls.blockingUnaryCall(channel, EchoServiceClient.ECHO_OPTIONAL_FIELDS, CallOptions.DEFAULT, request) + } + + override def echoWellKnownTypes(request: WellKnownTypesMessage): WellKnownTypesMessage = { + return ClientCalls.blockingUnaryCall(channel, EchoServiceClient.ECHO_WELL_KNOWN_TYPES, CallOptions.DEFAULT, request) + } + + override def echoPaymentMethod(request: PaymentMethod): PaymentMethod = { + return ClientCalls.blockingUnaryCall(channel, EchoServiceClient.ECHO_PAYMENT_METHOD, CallOptions.DEFAULT, request) + } + + override def echoNotification(request: Notification): Notification = { + return ClientCalls.blockingUnaryCall(channel, EchoServiceClient.ECHO_NOTIFICATION, CallOptions.DEFAULT, request) + } +} + +object EchoServiceClient { + val ECHO_CUSTOMER: MethodDescriptor[Customer, Customer] = MethodDescriptor.newBuilder(Customer.marshaller, Customer.marshaller).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoCustomer").build() + + val ECHO_INVENTORY: MethodDescriptor[Inventory, Inventory] = MethodDescriptor.newBuilder(Inventory.marshaller, Inventory.marshaller).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoInventory").build() + + val ECHO_NOTIFICATION: MethodDescriptor[Notification, Notification] = MethodDescriptor.newBuilder(Notification.marshaller, Notification.marshaller).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoNotification").build() + + val ECHO_OPTIONAL_FIELDS: MethodDescriptor[OptionalFields, OptionalFields] = MethodDescriptor.newBuilder(OptionalFields.marshaller, OptionalFields.marshaller).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoOptionalFields").build() + + val ECHO_ORDER: MethodDescriptor[Order, Order] = MethodDescriptor.newBuilder(Order.marshaller, Order.marshaller).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoOrder").build() + + val ECHO_OUTER: MethodDescriptor[Outer, Outer] = MethodDescriptor.newBuilder(Outer.marshaller, Outer.marshaller).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoOuter").build() + + val ECHO_PAYMENT_METHOD: MethodDescriptor[PaymentMethod, PaymentMethod] = MethodDescriptor.newBuilder(PaymentMethod.marshaller, PaymentMethod.marshaller).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoPaymentMethod").build() + + val ECHO_SCALAR_TYPES: MethodDescriptor[ScalarTypes, ScalarTypes] = MethodDescriptor.newBuilder(ScalarTypes.marshaller, ScalarTypes.marshaller).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoScalarTypes").build() + + val ECHO_WELL_KNOWN_TYPES: MethodDescriptor[WellKnownTypesMessage, WellKnownTypesMessage] = MethodDescriptor.newBuilder(WellKnownTypesMessage.marshaller, WellKnownTypesMessage.marshaller).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoWellKnownTypes").build() +} \ No newline at end of file diff --git a/testers/grpc/scala/generated-and-checked-in/com/example/grpc/EchoServiceServer.scala b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/EchoServiceServer.scala new file mode 100644 index 0000000000..83f68e1a5d --- /dev/null +++ b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/EchoServiceServer.scala @@ -0,0 +1,34 @@ +package com.example.grpc + +import io.grpc.BindableService +import io.grpc.MethodDescriptor +import io.grpc.MethodDescriptor.MethodType +import io.grpc.ServerServiceDefinition +import io.grpc.stub.ServerCalls + +/** gRPC server adapter for EchoService - delegates to clean service interface */ +class EchoServiceServer(val delegate: EchoService) extends BindableService { + override def bindService: ServerServiceDefinition = { + return ServerServiceDefinition.builder("testgrpc.EchoService").addMethod(EchoServiceServer.ECHO_SCALAR_TYPES, ServerCalls.asyncUnaryCall((request, responseObserver) => { responseObserver.onNext(delegate.echoScalarTypes(request)); responseObserver.onCompleted() })).addMethod(EchoServiceServer.ECHO_CUSTOMER, ServerCalls.asyncUnaryCall((request, responseObserver) => { responseObserver.onNext(delegate.echoCustomer(request)); responseObserver.onCompleted() })).addMethod(EchoServiceServer.ECHO_ORDER, ServerCalls.asyncUnaryCall((request, responseObserver) => { responseObserver.onNext(delegate.echoOrder(request)); responseObserver.onCompleted() })).addMethod(EchoServiceServer.ECHO_INVENTORY, ServerCalls.asyncUnaryCall((request, responseObserver) => { responseObserver.onNext(delegate.echoInventory(request)); responseObserver.onCompleted() })).addMethod(EchoServiceServer.ECHO_OUTER, ServerCalls.asyncUnaryCall((request, responseObserver) => { responseObserver.onNext(delegate.echoOuter(request)); responseObserver.onCompleted() })).addMethod(EchoServiceServer.ECHO_OPTIONAL_FIELDS, ServerCalls.asyncUnaryCall((request, responseObserver) => { responseObserver.onNext(delegate.echoOptionalFields(request)); responseObserver.onCompleted() })).addMethod(EchoServiceServer.ECHO_WELL_KNOWN_TYPES, ServerCalls.asyncUnaryCall((request, responseObserver) => { responseObserver.onNext(delegate.echoWellKnownTypes(request)); responseObserver.onCompleted() })).addMethod(EchoServiceServer.ECHO_PAYMENT_METHOD, ServerCalls.asyncUnaryCall((request, responseObserver) => { responseObserver.onNext(delegate.echoPaymentMethod(request)); responseObserver.onCompleted() })).addMethod(EchoServiceServer.ECHO_NOTIFICATION, ServerCalls.asyncUnaryCall((request, responseObserver) => { responseObserver.onNext(delegate.echoNotification(request)); responseObserver.onCompleted() })).build() + } +} + +object EchoServiceServer { + val ECHO_CUSTOMER: MethodDescriptor[Customer, Customer] = MethodDescriptor.newBuilder(Customer.marshaller, Customer.marshaller).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoCustomer").build() + + val ECHO_INVENTORY: MethodDescriptor[Inventory, Inventory] = MethodDescriptor.newBuilder(Inventory.marshaller, Inventory.marshaller).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoInventory").build() + + val ECHO_NOTIFICATION: MethodDescriptor[Notification, Notification] = MethodDescriptor.newBuilder(Notification.marshaller, Notification.marshaller).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoNotification").build() + + val ECHO_OPTIONAL_FIELDS: MethodDescriptor[OptionalFields, OptionalFields] = MethodDescriptor.newBuilder(OptionalFields.marshaller, OptionalFields.marshaller).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoOptionalFields").build() + + val ECHO_ORDER: MethodDescriptor[Order, Order] = MethodDescriptor.newBuilder(Order.marshaller, Order.marshaller).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoOrder").build() + + val ECHO_OUTER: MethodDescriptor[Outer, Outer] = MethodDescriptor.newBuilder(Outer.marshaller, Outer.marshaller).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoOuter").build() + + val ECHO_PAYMENT_METHOD: MethodDescriptor[PaymentMethod, PaymentMethod] = MethodDescriptor.newBuilder(PaymentMethod.marshaller, PaymentMethod.marshaller).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoPaymentMethod").build() + + val ECHO_SCALAR_TYPES: MethodDescriptor[ScalarTypes, ScalarTypes] = MethodDescriptor.newBuilder(ScalarTypes.marshaller, ScalarTypes.marshaller).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoScalarTypes").build() + + val ECHO_WELL_KNOWN_TYPES: MethodDescriptor[WellKnownTypesMessage, WellKnownTypesMessage] = MethodDescriptor.newBuilder(WellKnownTypesMessage.marshaller, WellKnownTypesMessage.marshaller).setType(MethodType.UNARY).setFullMethodName("testgrpc.EchoService/EchoWellKnownTypes").build() +} \ No newline at end of file diff --git a/testers/grpc/scala/generated-and-checked-in/com/example/grpc/GetCustomerRequest.scala b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/GetCustomerRequest.scala new file mode 100644 index 0000000000..76c30c9a19 --- /dev/null +++ b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/GetCustomerRequest.scala @@ -0,0 +1,59 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +case class GetCustomerRequest(customerId: String) { + @throws[IOException] + def writeTo(output: CodedOutputStream): Unit = { + output.writeString(1, this.customerId) + } + + def getSerializedSize: Int = { + var size: Int = 0 + size = size + CodedOutputStream.computeStringSize(1, this.customerId) + return size + } +} + +object GetCustomerRequest { + given marshaller: Marshaller[GetCustomerRequest] = { + new Marshaller[GetCustomerRequest] { + override def stream(value: GetCustomerRequest): InputStream = { + val bytes = Array.ofDim[Byte](value.getSerializedSize) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch { + case e: IOException => throw new RuntimeException(e) + } + return new ByteArrayInputStream(bytes) + } + override def parse(stream: InputStream): GetCustomerRequest = { + try { + return GetCustomerRequest.parseFrom(CodedInputStream.newInstance(stream)) + } catch { + case e: IOException => throw new RuntimeException(e) + } + } + } + } + + @throws[IOException] + def parseFrom(input: CodedInputStream): GetCustomerRequest = { + var customerId: String = "" + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { customerId = input.readString() } + else { input.skipField(tag) } + } + return new GetCustomerRequest(customerId) + } +} \ No newline at end of file diff --git a/testers/grpc/scala/generated-and-checked-in/com/example/grpc/GetCustomerResponse.scala b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/GetCustomerResponse.scala new file mode 100644 index 0000000000..917be4e6df --- /dev/null +++ b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/GetCustomerResponse.scala @@ -0,0 +1,68 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +case class GetCustomerResponse(customer: Customer) { + @throws[IOException] + def writeTo(output: CodedOutputStream): Unit = { + if ((this.customer != null)) { + output.writeTag(1, 2); + output.writeUInt32NoTag(this.customer.getSerializedSize); + this.customer.writeTo(output); + } + } + + def getSerializedSize: Int = { + var size: Int = 0 + if ((this.customer != null)) { + size = size + CodedOutputStream.computeTagSize(1) + CodedOutputStream.computeUInt32SizeNoTag(this.customer.getSerializedSize) + this.customer.getSerializedSize + } + return size + } +} + +object GetCustomerResponse { + given marshaller: Marshaller[GetCustomerResponse] = { + new Marshaller[GetCustomerResponse] { + override def stream(value: GetCustomerResponse): InputStream = { + val bytes = Array.ofDim[Byte](value.getSerializedSize) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch { + case e: IOException => throw new RuntimeException(e) + } + return new ByteArrayInputStream(bytes) + } + override def parse(stream: InputStream): GetCustomerResponse = { + try { + return GetCustomerResponse.parseFrom(CodedInputStream.newInstance(stream)) + } catch { + case e: IOException => throw new RuntimeException(e) + } + } + } + } + + @throws[IOException] + def parseFrom(input: CodedInputStream): GetCustomerResponse = { + var customer: Customer = null + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { val `_length` = input.readRawVarint32(); + val `_oldLimit` = input.pushLimit(`_length`); + customer = Customer.parseFrom(input); + input.popLimit(`_oldLimit`); } + else { input.skipField(tag) } + } + return new GetCustomerResponse(customer) + } +} \ No newline at end of file diff --git a/testers/grpc/scala/generated-and-checked-in/com/example/grpc/Inner.scala b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/Inner.scala new file mode 100644 index 0000000000..9719261aac --- /dev/null +++ b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/Inner.scala @@ -0,0 +1,66 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +case class Inner( + value: Int, + description: String +) { + @throws[IOException] + def writeTo(output: CodedOutputStream): Unit = { + output.writeInt32(1, this.value) + output.writeString(2, this.description) + } + + def getSerializedSize: Int = { + var size: Int = 0 + size = size + CodedOutputStream.computeInt32Size(1, this.value) + size = size + CodedOutputStream.computeStringSize(2, this.description) + return size + } +} + +object Inner { + given marshaller: Marshaller[Inner] = { + new Marshaller[Inner] { + override def stream(value: Inner): InputStream = { + val bytes = Array.ofDim[Byte](value.getSerializedSize) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch { + case e: IOException => throw new RuntimeException(e) + } + return new ByteArrayInputStream(bytes) + } + override def parse(stream: InputStream): Inner = { + try { + return Inner.parseFrom(CodedInputStream.newInstance(stream)) + } catch { + case e: IOException => throw new RuntimeException(e) + } + } + } + } + + @throws[IOException] + def parseFrom(input: CodedInputStream): Inner = { + var value: Int = 0 + var description: String = "" + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { value = input.readInt32() } + else if (WireFormat.getTagFieldNumber(tag) == 2) { description = input.readString() } + else { input.skipField(tag) } + } + return new Inner(value, description) + } +} \ No newline at end of file diff --git a/testers/grpc/scala/generated-and-checked-in/com/example/grpc/Inventory.scala b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/Inventory.scala new file mode 100644 index 0000000000..ce2d6ec21e --- /dev/null +++ b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/Inventory.scala @@ -0,0 +1,113 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException +import scala.collection.mutable.ListBuffer + +case class Inventory( + warehouseId: String, + productIds: List[String], + stockCounts: Map[String, Int], + recentOrders: List[Order] +) { + @throws[IOException] + def writeTo(output: CodedOutputStream): Unit = { + output.writeString(1, this.warehouseId) + for (elem <- this.productIds) { + output.writeString(2, elem); + } + for ((k, v) <- this.stockCounts) { + output.writeTag(3, 2); + output.writeUInt32NoTag(CodedOutputStream.computeStringSize(1, k) + CodedOutputStream.computeInt32Size(2, v)); + output.writeString(1, k); + output.writeInt32(2, v); + } + for (elem <- this.recentOrders) { + output.writeTag(4, 2); + output.writeUInt32NoTag(elem.getSerializedSize); + elem.writeTo(output); + } + } + + def getSerializedSize: Int = { + var size: Int = 0 + size = size + CodedOutputStream.computeStringSize(1, this.warehouseId) + for (elem <- this.productIds) { + size = size + CodedOutputStream.computeStringSize(2, elem); + } + for ((k, v) <- this.stockCounts) { + size = size + CodedOutputStream.computeTagSize(3) + CodedOutputStream.computeUInt32SizeNoTag(CodedOutputStream.computeStringSize(1, k) + CodedOutputStream.computeInt32Size(2, v)) + CodedOutputStream.computeStringSize(1, k) + CodedOutputStream.computeInt32Size(2, v); + } + for (elem <- this.recentOrders) { + size = size + CodedOutputStream.computeTagSize(4) + CodedOutputStream.computeUInt32SizeNoTag(elem.getSerializedSize) + elem.getSerializedSize; + } + return size + } +} + +object Inventory { + given marshaller: Marshaller[Inventory] = { + new Marshaller[Inventory] { + override def stream(value: Inventory): InputStream = { + val bytes = Array.ofDim[Byte](value.getSerializedSize) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch { + case e: IOException => throw new RuntimeException(e) + } + return new ByteArrayInputStream(bytes) + } + override def parse(stream: InputStream): Inventory = { + try { + return Inventory.parseFrom(CodedInputStream.newInstance(stream)) + } catch { + case e: IOException => throw new RuntimeException(e) + } + } + } + } + + @throws[IOException] + def parseFrom(input: CodedInputStream): Inventory = { + var warehouseId: String = "" + var productIds: ListBuffer[String] = ListBuffer() + var stockCounts: scala.collection.mutable.Map[String, Int] = scala.collection.mutable.Map.empty[String, Int] + var recentOrders: ListBuffer[Order] = ListBuffer() + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { warehouseId = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 2) { productIds.addOne(input.readString()): @scala.annotation.nowarn } + else if (WireFormat.getTagFieldNumber(tag) == 3) { val length = input.readRawVarint32(); + val oldLimit = input.pushLimit(length); + var mapKey = ""; + var mapValue = 0; + while (!input.isAtEnd()) { + val entryTag = input.readTag() + if (WireFormat.getTagFieldNumber(entryTag) == 1) { mapKey = input.readString() } + else if (WireFormat.getTagFieldNumber(entryTag) == 2) { mapValue = input.readInt32() } + else { input.skipField(entryTag) } + }; + input.popLimit(oldLimit); + stockCounts.put(mapKey, mapValue): @scala.annotation.nowarn; } + else if (WireFormat.getTagFieldNumber(tag) == 4) { val `_length` = input.readRawVarint32(); + val `_oldLimit` = input.pushLimit(`_length`); + recentOrders.addOne(Order.parseFrom(input)): @scala.annotation.nowarn; + input.popLimit(`_oldLimit`); } + else { input.skipField(tag) } + } + return new Inventory( + warehouseId, + productIds.toList, + stockCounts.toMap, + recentOrders.toList + ) + } +} \ No newline at end of file diff --git a/testers/grpc/scala/generated-and-checked-in/com/example/grpc/ListOrdersRequest.scala b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/ListOrdersRequest.scala new file mode 100644 index 0000000000..ff55b8e376 --- /dev/null +++ b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/ListOrdersRequest.scala @@ -0,0 +1,66 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +case class ListOrdersRequest( + customerId: String, + pageSize: Int +) { + @throws[IOException] + def writeTo(output: CodedOutputStream): Unit = { + output.writeString(1, this.customerId) + output.writeInt32(2, this.pageSize) + } + + def getSerializedSize: Int = { + var size: Int = 0 + size = size + CodedOutputStream.computeStringSize(1, this.customerId) + size = size + CodedOutputStream.computeInt32Size(2, this.pageSize) + return size + } +} + +object ListOrdersRequest { + given marshaller: Marshaller[ListOrdersRequest] = { + new Marshaller[ListOrdersRequest] { + override def stream(value: ListOrdersRequest): InputStream = { + val bytes = Array.ofDim[Byte](value.getSerializedSize) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch { + case e: IOException => throw new RuntimeException(e) + } + return new ByteArrayInputStream(bytes) + } + override def parse(stream: InputStream): ListOrdersRequest = { + try { + return ListOrdersRequest.parseFrom(CodedInputStream.newInstance(stream)) + } catch { + case e: IOException => throw new RuntimeException(e) + } + } + } + } + + @throws[IOException] + def parseFrom(input: CodedInputStream): ListOrdersRequest = { + var customerId: String = "" + var pageSize: Int = 0 + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { customerId = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 2) { pageSize = input.readInt32() } + else { input.skipField(tag) } + } + return new ListOrdersRequest(customerId, pageSize) + } +} \ No newline at end of file diff --git a/testers/grpc/scala/generated-and-checked-in/com/example/grpc/Notification.scala b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/Notification.scala new file mode 100644 index 0000000000..b8df5b6a37 --- /dev/null +++ b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/Notification.scala @@ -0,0 +1,86 @@ +package com.example.grpc + +import com.example.grpc.NotificationTarget.Email +import com.example.grpc.NotificationTarget.Phone +import com.example.grpc.NotificationTarget.WebhookUrl +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +case class Notification( + message: String, + priority: Priority, + target: NotificationTarget +) { + @throws[IOException] + def writeTo(output: CodedOutputStream): Unit = { + output.writeString(1, this.message) + output.writeEnum(2, this.priority.toValue) + this.target match { + case null => {} + case c: Email => output.writeString(3, c.email) + case c: Phone => output.writeString(4, c.phone) + case c: WebhookUrl => output.writeString(5, c.webhookUrl) + } + } + + def getSerializedSize: Int = { + var size: Int = 0 + size = size + CodedOutputStream.computeStringSize(1, this.message) + size = size + CodedOutputStream.computeEnumSize(2, this.priority.toValue) + this.target match { + case null => {} + case c: Email => size = size + CodedOutputStream.computeStringSize(3, c.email) + case c: Phone => size = size + CodedOutputStream.computeStringSize(4, c.phone) + case c: WebhookUrl => size = size + CodedOutputStream.computeStringSize(5, c.webhookUrl) + } + return size + } +} + +object Notification { + given marshaller: Marshaller[Notification] = { + new Marshaller[Notification] { + override def stream(value: Notification): InputStream = { + val bytes = Array.ofDim[Byte](value.getSerializedSize) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch { + case e: IOException => throw new RuntimeException(e) + } + return new ByteArrayInputStream(bytes) + } + override def parse(stream: InputStream): Notification = { + try { + return Notification.parseFrom(CodedInputStream.newInstance(stream)) + } catch { + case e: IOException => throw new RuntimeException(e) + } + } + } + } + + @throws[IOException] + def parseFrom(input: CodedInputStream): Notification = { + var message: String = "" + var priority: Priority = Priority.fromValue(0) + var target: NotificationTarget = null + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { message = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 2) { priority = Priority.fromValue(input.readEnum()) } + else if (WireFormat.getTagFieldNumber(tag) == 3) { target = new Email(input.readString()) } + else if (WireFormat.getTagFieldNumber(tag) == 4) { target = new Phone(input.readString()) } + else if (WireFormat.getTagFieldNumber(tag) == 5) { target = new WebhookUrl(input.readString()) } + else { input.skipField(tag) } + } + return new Notification(message, priority, target) + } +} \ No newline at end of file diff --git a/testers/grpc/scala/generated-and-checked-in/com/example/grpc/NotificationTarget.scala b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/NotificationTarget.scala new file mode 100644 index 0000000000..9e0d369ad9 --- /dev/null +++ b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/NotificationTarget.scala @@ -0,0 +1,14 @@ +package com.example.grpc + + + +/** OneOf type for target */ +sealed trait NotificationTarget + +object NotificationTarget { + case class Email(email: String) extends NotificationTarget + + case class Phone(phone: String) extends NotificationTarget + + case class WebhookUrl(webhookUrl: String) extends NotificationTarget +} \ No newline at end of file diff --git a/testers/grpc/scala/generated-and-checked-in/com/example/grpc/OptionalFields.scala b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/OptionalFields.scala new file mode 100644 index 0000000000..faae7e2476 --- /dev/null +++ b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/OptionalFields.scala @@ -0,0 +1,94 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +case class OptionalFields( + name: Option[String], + age: Option[Int], + customer: Option[Customer] +) { + @throws[IOException] + def writeTo(output: CodedOutputStream): Unit = { + if (this.name.isDefined) { + val v = this.name.get; + output.writeString(1, v); + } + if (this.age.isDefined) { + val v = this.age.get; + output.writeInt32(2, v); + } + if (this.customer.isDefined) { + val v = this.customer.get; + output.writeTag(3, 2); + output.writeUInt32NoTag(v.getSerializedSize); + v.writeTo(output); + } + } + + def getSerializedSize: Int = { + var size: Int = 0 + if (this.name.isDefined) { + val v = this.name.get; + size = size + CodedOutputStream.computeStringSize(1, v); + } + if (this.age.isDefined) { + val v = this.age.get; + size = size + CodedOutputStream.computeInt32Size(2, v); + } + if (this.customer.isDefined) { + val v = this.customer.get; + size = size + CodedOutputStream.computeTagSize(3) + CodedOutputStream.computeUInt32SizeNoTag(v.getSerializedSize) + v.getSerializedSize; + } + return size + } +} + +object OptionalFields { + given marshaller: Marshaller[OptionalFields] = { + new Marshaller[OptionalFields] { + override def stream(value: OptionalFields): InputStream = { + val bytes = Array.ofDim[Byte](value.getSerializedSize) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch { + case e: IOException => throw new RuntimeException(e) + } + return new ByteArrayInputStream(bytes) + } + override def parse(stream: InputStream): OptionalFields = { + try { + return OptionalFields.parseFrom(CodedInputStream.newInstance(stream)) + } catch { + case e: IOException => throw new RuntimeException(e) + } + } + } + } + + @throws[IOException] + def parseFrom(input: CodedInputStream): OptionalFields = { + var name: Option[String] = None + var age: Option[Int] = None + var customer: Option[Customer] = None + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { name = Some(input.readString()) } + else if (WireFormat.getTagFieldNumber(tag) == 2) { age = Some(input.readInt32()) } + else if (WireFormat.getTagFieldNumber(tag) == 3) { val `_length` = input.readRawVarint32(); + val `_oldLimit` = input.pushLimit(`_length`); + customer = Some(Customer.parseFrom(input)); + input.popLimit(`_oldLimit`); } + else { input.skipField(tag) } + } + return new OptionalFields(name, age, customer) + } +} \ No newline at end of file diff --git a/testers/grpc/scala/generated-and-checked-in/com/example/grpc/Order.scala b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/Order.scala new file mode 100644 index 0000000000..72d09c47e7 --- /dev/null +++ b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/Order.scala @@ -0,0 +1,96 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException +import java.time.Instant + +case class Order( + orderId: OrderId, + customerId: CustomerId, + amountCents: Long, + createdAt: Instant +) { + @throws[IOException] + def writeTo(output: CodedOutputStream): Unit = { + output.writeString(1, this.orderId.unwrap) + output.writeString(2, this.customerId.unwrap) + output.writeInt64(3, this.amountCents) + output.writeTag(4, 2) + output.writeUInt32NoTag(CodedOutputStream.computeInt64Size(1, this.createdAt.getEpochSecond()) + CodedOutputStream.computeInt32Size(2, this.createdAt.getNano())) + output.writeInt64(1, this.createdAt.getEpochSecond()) + output.writeInt32(2, this.createdAt.getNano()) + } + + def getSerializedSize: Int = { + var size: Int = 0 + size = size + CodedOutputStream.computeStringSize(1, this.orderId.unwrap) + size = size + CodedOutputStream.computeStringSize(2, this.customerId.unwrap) + size = size + CodedOutputStream.computeInt64Size(3, this.amountCents) + size = size + CodedOutputStream.computeTagSize(4) + CodedOutputStream.computeUInt32SizeNoTag(CodedOutputStream.computeInt64Size(1, this.createdAt.getEpochSecond()) + CodedOutputStream.computeInt32Size(2, this.createdAt.getNano())) + CodedOutputStream.computeInt64Size(1, this.createdAt.getEpochSecond()) + CodedOutputStream.computeInt32Size(2, this.createdAt.getNano()) + return size + } +} + +object Order { + given marshaller: Marshaller[Order] = { + new Marshaller[Order] { + override def stream(value: Order): InputStream = { + val bytes = Array.ofDim[Byte](value.getSerializedSize) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch { + case e: IOException => throw new RuntimeException(e) + } + return new ByteArrayInputStream(bytes) + } + override def parse(stream: InputStream): Order = { + try { + return Order.parseFrom(CodedInputStream.newInstance(stream)) + } catch { + case e: IOException => throw new RuntimeException(e) + } + } + } + } + + @throws[IOException] + def parseFrom(input: CodedInputStream): Order = { + var orderId: OrderId = OrderId.valueOf("") + var customerId: CustomerId = CustomerId.valueOf("") + var amountCents: Long = 0L + var createdAt: Instant = Instant.EPOCH + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { orderId = OrderId.valueOf(input.readString()) } + else if (WireFormat.getTagFieldNumber(tag) == 2) { customerId = CustomerId.valueOf(input.readString()) } + else if (WireFormat.getTagFieldNumber(tag) == 3) { amountCents = input.readInt64() } + else if (WireFormat.getTagFieldNumber(tag) == 4) { val `_length` = input.readRawVarint32(); + val `_oldLimit` = input.pushLimit(`_length`); + var `_tsSeconds` = 0L; + var `_tsNanos` = 0; + while (!input.isAtEnd()) { + val `_tsTag` = input.readTag() + if (WireFormat.getTagFieldNumber(`_tsTag`) == 1) { `_tsSeconds` = input.readInt64() } + else if (WireFormat.getTagFieldNumber(`_tsTag`) == 2) { `_tsNanos` = input.readInt32() } + else { input.skipField(`_tsTag`) } + }; + createdAt = Instant.ofEpochSecond(`_tsSeconds`, `_tsNanos`.toLong); + input.popLimit(`_oldLimit`); } + else { input.skipField(tag) } + } + return new Order( + orderId, + customerId, + amountCents, + createdAt + ) + } +} \ No newline at end of file diff --git a/testers/grpc/scala/generated-and-checked-in/com/example/grpc/OrderId.scala b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/OrderId.scala new file mode 100644 index 0000000000..804c53f3ab --- /dev/null +++ b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/OrderId.scala @@ -0,0 +1,18 @@ +package com.example.grpc + + + +/** Wrapper type for typr.grpc.GrpcCodegen$$$Lambda/0x00007fc001118400@65987993 */ +case class OrderId(value: String) extends scala.AnyVal { + /** Get the underlying value */ + def unwrap: String = { + return this.value + } +} + +object OrderId { + /** Create a OrderId from a raw value */ + def valueOf(v: String): OrderId = { + return new OrderId(v) + } +} \ No newline at end of file diff --git a/testers/grpc/scala/generated-and-checked-in/com/example/grpc/OrderService.scala b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/OrderService.scala new file mode 100644 index 0000000000..a743272fc4 --- /dev/null +++ b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/OrderService.scala @@ -0,0 +1,16 @@ +package com.example.grpc + + + +/** Clean service interface for OrderService gRPC service */ +trait OrderService { + def getCustomer(request: GetCustomerRequest): GetCustomerResponse + + def createOrder(request: CreateOrderRequest): CreateOrderResponse + + def listOrders(request: ListOrdersRequest): java.util.Iterator[OrderUpdate] + + def submitOrders(requests: java.util.Iterator[CreateOrderRequest]): OrderSummary + + def chat(requests: java.util.Iterator[ChatMessage]): java.util.Iterator[ChatMessage] +} \ No newline at end of file diff --git a/testers/grpc/scala/generated-and-checked-in/com/example/grpc/OrderServiceClient.scala b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/OrderServiceClient.scala new file mode 100644 index 0000000000..3beaeed6fe --- /dev/null +++ b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/OrderServiceClient.scala @@ -0,0 +1,43 @@ +package com.example.grpc + +import io.grpc.CallOptions +import io.grpc.Channel +import io.grpc.MethodDescriptor +import io.grpc.MethodDescriptor.MethodType +import io.grpc.stub.ClientCalls +import java.lang.UnsupportedOperationException + +/** gRPC client wrapper for OrderService - wraps Channel with clean types */ +class OrderServiceClient(val channel: Channel) extends OrderService { + override def getCustomer(request: GetCustomerRequest): GetCustomerResponse = { + return ClientCalls.blockingUnaryCall(channel, OrderServiceClient.GET_CUSTOMER, CallOptions.DEFAULT, request) + } + + override def createOrder(request: CreateOrderRequest): CreateOrderResponse = { + return ClientCalls.blockingUnaryCall(channel, OrderServiceClient.CREATE_ORDER, CallOptions.DEFAULT, request) + } + + override def listOrders(request: ListOrdersRequest): java.util.Iterator[OrderUpdate] = { + return ClientCalls.blockingServerStreamingCall(channel, OrderServiceClient.LIST_ORDERS, CallOptions.DEFAULT, request) + } + + override def submitOrders(requests: java.util.Iterator[CreateOrderRequest]): OrderSummary = { + throw new UnsupportedOperationException("Client streaming not yet implemented in client wrapper") + } + + override def chat(requests: java.util.Iterator[ChatMessage]): java.util.Iterator[ChatMessage] = { + throw new UnsupportedOperationException("Bidi streaming not yet implemented in client wrapper") + } +} + +object OrderServiceClient { + val CHAT: MethodDescriptor[ChatMessage, ChatMessage] = MethodDescriptor.newBuilder(ChatMessage.marshaller, ChatMessage.marshaller).setType(MethodType.BIDI_STREAMING).setFullMethodName("testgrpc.OrderService/Chat").build() + + val CREATE_ORDER: MethodDescriptor[CreateOrderRequest, CreateOrderResponse] = MethodDescriptor.newBuilder(CreateOrderRequest.marshaller, CreateOrderResponse.marshaller).setType(MethodType.UNARY).setFullMethodName("testgrpc.OrderService/CreateOrder").build() + + val GET_CUSTOMER: MethodDescriptor[GetCustomerRequest, GetCustomerResponse] = MethodDescriptor.newBuilder(GetCustomerRequest.marshaller, GetCustomerResponse.marshaller).setType(MethodType.UNARY).setFullMethodName("testgrpc.OrderService/GetCustomer").build() + + val LIST_ORDERS: MethodDescriptor[ListOrdersRequest, OrderUpdate] = MethodDescriptor.newBuilder(ListOrdersRequest.marshaller, OrderUpdate.marshaller).setType(MethodType.SERVER_STREAMING).setFullMethodName("testgrpc.OrderService/ListOrders").build() + + val SUBMIT_ORDERS: MethodDescriptor[CreateOrderRequest, OrderSummary] = MethodDescriptor.newBuilder(CreateOrderRequest.marshaller, OrderSummary.marshaller).setType(MethodType.CLIENT_STREAMING).setFullMethodName("testgrpc.OrderService/SubmitOrders").build() +} \ No newline at end of file diff --git a/testers/grpc/scala/generated-and-checked-in/com/example/grpc/OrderServiceServer.scala b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/OrderServiceServer.scala new file mode 100644 index 0000000000..9bf3b4077d --- /dev/null +++ b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/OrderServiceServer.scala @@ -0,0 +1,29 @@ +package com.example.grpc + +import io.grpc.BindableService +import io.grpc.MethodDescriptor +import io.grpc.MethodDescriptor.MethodType +import io.grpc.ServerServiceDefinition +import io.grpc.stub.ServerCalls +import java.lang.UnsupportedOperationException + +/** gRPC server adapter for OrderService - delegates to clean service interface */ +class OrderServiceServer(val delegate: OrderService) extends BindableService { + override def bindService: ServerServiceDefinition = { + return ServerServiceDefinition.builder("testgrpc.OrderService").addMethod(OrderServiceServer.GET_CUSTOMER, ServerCalls.asyncUnaryCall((request, responseObserver) => { responseObserver.onNext(delegate.getCustomer(request)); responseObserver.onCompleted() })).addMethod(OrderServiceServer.CREATE_ORDER, ServerCalls.asyncUnaryCall((request, responseObserver) => { responseObserver.onNext(delegate.createOrder(request)); responseObserver.onCompleted() })).addMethod(OrderServiceServer.LIST_ORDERS, ServerCalls.asyncServerStreamingCall((request, responseObserver) => { val results = delegate.listOrders(request); while (results.hasNext()) { + responseObserver.onNext(results.next()) + }; responseObserver.onCompleted() })).addMethod(OrderServiceServer.SUBMIT_ORDERS, ServerCalls.asyncClientStreamingCall(responseObserver => { throw new UnsupportedOperationException("Client streaming not yet implemented in server adapter") })).addMethod(OrderServiceServer.CHAT, ServerCalls.asyncBidiStreamingCall(responseObserver => { throw new UnsupportedOperationException("Bidi streaming not yet implemented in server adapter") })).build() + } +} + +object OrderServiceServer { + val CHAT: MethodDescriptor[ChatMessage, ChatMessage] = MethodDescriptor.newBuilder(ChatMessage.marshaller, ChatMessage.marshaller).setType(MethodType.BIDI_STREAMING).setFullMethodName("testgrpc.OrderService/Chat").build() + + val CREATE_ORDER: MethodDescriptor[CreateOrderRequest, CreateOrderResponse] = MethodDescriptor.newBuilder(CreateOrderRequest.marshaller, CreateOrderResponse.marshaller).setType(MethodType.UNARY).setFullMethodName("testgrpc.OrderService/CreateOrder").build() + + val GET_CUSTOMER: MethodDescriptor[GetCustomerRequest, GetCustomerResponse] = MethodDescriptor.newBuilder(GetCustomerRequest.marshaller, GetCustomerResponse.marshaller).setType(MethodType.UNARY).setFullMethodName("testgrpc.OrderService/GetCustomer").build() + + val LIST_ORDERS: MethodDescriptor[ListOrdersRequest, OrderUpdate] = MethodDescriptor.newBuilder(ListOrdersRequest.marshaller, OrderUpdate.marshaller).setType(MethodType.SERVER_STREAMING).setFullMethodName("testgrpc.OrderService/ListOrders").build() + + val SUBMIT_ORDERS: MethodDescriptor[CreateOrderRequest, OrderSummary] = MethodDescriptor.newBuilder(CreateOrderRequest.marshaller, OrderSummary.marshaller).setType(MethodType.CLIENT_STREAMING).setFullMethodName("testgrpc.OrderService/SubmitOrders").build() +} \ No newline at end of file diff --git a/testers/grpc/scala/generated-and-checked-in/com/example/grpc/OrderStatus.scala b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/OrderStatus.scala new file mode 100644 index 0000000000..bd348911ad --- /dev/null +++ b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/OrderStatus.scala @@ -0,0 +1,36 @@ +package com.example.grpc + +import java.lang.IllegalArgumentException + + +enum OrderStatus { + case ORDER_STATUS_UNSPECIFIED, ORDER_STATUS_PENDING, ORDER_STATUS_PROCESSING, ORDER_STATUS_SHIPPED, ORDER_STATUS_DELIVERED, ORDER_STATUS_CANCELLED + def toValue: Int = { + if (this.toString.equals("ORDER_STATUS_UNSPECIFIED")) { return 0 } + else if (this.toString.equals("ORDER_STATUS_PENDING")) { return 1 } + else if (this.toString.equals("ORDER_STATUS_PROCESSING")) { return 2 } + else if (this.toString.equals("ORDER_STATUS_SHIPPED")) { return 3 } + else if (this.toString.equals("ORDER_STATUS_DELIVERED")) { return 4 } + else if (this.toString.equals("ORDER_STATUS_CANCELLED")) { return 5 } + else { return 0 } + } +} + +object OrderStatus { + def fromValue(value: Int): OrderStatus = { + if (value == 0) { return OrderStatus.ORDER_STATUS_UNSPECIFIED } + else if (value == 1) { return OrderStatus.ORDER_STATUS_PENDING } + else if (value == 2) { return OrderStatus.ORDER_STATUS_PROCESSING } + else if (value == 3) { return OrderStatus.ORDER_STATUS_SHIPPED } + else if (value == 4) { return OrderStatus.ORDER_STATUS_DELIVERED } + else if (value == 5) { return OrderStatus.ORDER_STATUS_CANCELLED } + else { throw new IllegalArgumentException("Unknown enum value: " + value) } + } + extension (e: OrderStatus) def value: java.lang.String = e.toString + def apply(str: java.lang.String): scala.Either[java.lang.String, OrderStatus] = + scala.util.Try(OrderStatus.valueOf(str)).toEither.left.map(_ => s"'$str' does not match any of the following legal values: $Names") + def force(str: java.lang.String): OrderStatus = OrderStatus.valueOf(str) + val All: scala.List[OrderStatus] = values.toList + val Names: java.lang.String = All.map(_.toString).mkString(", ") + val ByName: scala.collection.immutable.Map[java.lang.String, OrderStatus] = All.map(x => (x.toString, x)).toMap +} \ No newline at end of file diff --git a/testers/grpc/scala/generated-and-checked-in/com/example/grpc/OrderSummary.scala b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/OrderSummary.scala new file mode 100644 index 0000000000..2630552ee2 --- /dev/null +++ b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/OrderSummary.scala @@ -0,0 +1,66 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +case class OrderSummary( + totalOrders: Int, + totalAmountCents: Long +) { + @throws[IOException] + def writeTo(output: CodedOutputStream): Unit = { + output.writeInt32(1, this.totalOrders) + output.writeInt64(2, this.totalAmountCents) + } + + def getSerializedSize: Int = { + var size: Int = 0 + size = size + CodedOutputStream.computeInt32Size(1, this.totalOrders) + size = size + CodedOutputStream.computeInt64Size(2, this.totalAmountCents) + return size + } +} + +object OrderSummary { + given marshaller: Marshaller[OrderSummary] = { + new Marshaller[OrderSummary] { + override def stream(value: OrderSummary): InputStream = { + val bytes = Array.ofDim[Byte](value.getSerializedSize) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch { + case e: IOException => throw new RuntimeException(e) + } + return new ByteArrayInputStream(bytes) + } + override def parse(stream: InputStream): OrderSummary = { + try { + return OrderSummary.parseFrom(CodedInputStream.newInstance(stream)) + } catch { + case e: IOException => throw new RuntimeException(e) + } + } + } + } + + @throws[IOException] + def parseFrom(input: CodedInputStream): OrderSummary = { + var totalOrders: Int = 0 + var totalAmountCents: Long = 0L + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { totalOrders = input.readInt32() } + else if (WireFormat.getTagFieldNumber(tag) == 2) { totalAmountCents = input.readInt64() } + else { input.skipField(tag) } + } + return new OrderSummary(totalOrders, totalAmountCents) + } +} \ No newline at end of file diff --git a/testers/grpc/scala/generated-and-checked-in/com/example/grpc/OrderUpdate.scala b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/OrderUpdate.scala new file mode 100644 index 0000000000..8bd38f1b1c --- /dev/null +++ b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/OrderUpdate.scala @@ -0,0 +1,86 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException +import java.time.Instant + +case class OrderUpdate( + orderId: String, + status: OrderStatus, + updatedAt: Instant +) { + @throws[IOException] + def writeTo(output: CodedOutputStream): Unit = { + output.writeString(1, this.orderId) + output.writeEnum(2, this.status.toValue) + output.writeTag(3, 2) + output.writeUInt32NoTag(CodedOutputStream.computeInt64Size(1, this.updatedAt.getEpochSecond()) + CodedOutputStream.computeInt32Size(2, this.updatedAt.getNano())) + output.writeInt64(1, this.updatedAt.getEpochSecond()) + output.writeInt32(2, this.updatedAt.getNano()) + } + + def getSerializedSize: Int = { + var size: Int = 0 + size = size + CodedOutputStream.computeStringSize(1, this.orderId) + size = size + CodedOutputStream.computeEnumSize(2, this.status.toValue) + size = size + CodedOutputStream.computeTagSize(3) + CodedOutputStream.computeUInt32SizeNoTag(CodedOutputStream.computeInt64Size(1, this.updatedAt.getEpochSecond()) + CodedOutputStream.computeInt32Size(2, this.updatedAt.getNano())) + CodedOutputStream.computeInt64Size(1, this.updatedAt.getEpochSecond()) + CodedOutputStream.computeInt32Size(2, this.updatedAt.getNano()) + return size + } +} + +object OrderUpdate { + given marshaller: Marshaller[OrderUpdate] = { + new Marshaller[OrderUpdate] { + override def stream(value: OrderUpdate): InputStream = { + val bytes = Array.ofDim[Byte](value.getSerializedSize) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch { + case e: IOException => throw new RuntimeException(e) + } + return new ByteArrayInputStream(bytes) + } + override def parse(stream: InputStream): OrderUpdate = { + try { + return OrderUpdate.parseFrom(CodedInputStream.newInstance(stream)) + } catch { + case e: IOException => throw new RuntimeException(e) + } + } + } + } + + @throws[IOException] + def parseFrom(input: CodedInputStream): OrderUpdate = { + var orderId: String = "" + var status: OrderStatus = OrderStatus.fromValue(0) + var updatedAt: Instant = Instant.EPOCH + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { orderId = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 2) { status = OrderStatus.fromValue(input.readEnum()) } + else if (WireFormat.getTagFieldNumber(tag) == 3) { val `_length` = input.readRawVarint32(); + val `_oldLimit` = input.pushLimit(`_length`); + var `_tsSeconds` = 0L; + var `_tsNanos` = 0; + while (!input.isAtEnd()) { + val `_tsTag` = input.readTag() + if (WireFormat.getTagFieldNumber(`_tsTag`) == 1) { `_tsSeconds` = input.readInt64() } + else if (WireFormat.getTagFieldNumber(`_tsTag`) == 2) { `_tsNanos` = input.readInt32() } + else { input.skipField(`_tsTag`) } + }; + updatedAt = Instant.ofEpochSecond(`_tsSeconds`, `_tsNanos`.toLong); + input.popLimit(`_oldLimit`); } + else { input.skipField(tag) } + } + return new OrderUpdate(orderId, status, updatedAt) + } +} \ No newline at end of file diff --git a/testers/grpc/scala/generated-and-checked-in/com/example/grpc/Outer.scala b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/Outer.scala new file mode 100644 index 0000000000..8d3dff6d84 --- /dev/null +++ b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/Outer.scala @@ -0,0 +1,75 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +case class Outer( + name: String, + inner: Inner +) { + @throws[IOException] + def writeTo(output: CodedOutputStream): Unit = { + output.writeString(1, this.name) + if ((this.inner != null)) { + output.writeTag(2, 2); + output.writeUInt32NoTag(this.inner.getSerializedSize); + this.inner.writeTo(output); + } + } + + def getSerializedSize: Int = { + var size: Int = 0 + size = size + CodedOutputStream.computeStringSize(1, this.name) + if ((this.inner != null)) { + size = size + CodedOutputStream.computeTagSize(2) + CodedOutputStream.computeUInt32SizeNoTag(this.inner.getSerializedSize) + this.inner.getSerializedSize + } + return size + } +} + +object Outer { + given marshaller: Marshaller[Outer] = { + new Marshaller[Outer] { + override def stream(value: Outer): InputStream = { + val bytes = Array.ofDim[Byte](value.getSerializedSize) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch { + case e: IOException => throw new RuntimeException(e) + } + return new ByteArrayInputStream(bytes) + } + override def parse(stream: InputStream): Outer = { + try { + return Outer.parseFrom(CodedInputStream.newInstance(stream)) + } catch { + case e: IOException => throw new RuntimeException(e) + } + } + } + } + + @throws[IOException] + def parseFrom(input: CodedInputStream): Outer = { + var name: String = "" + var inner: Inner = null + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { name = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 2) { val `_length` = input.readRawVarint32(); + val `_oldLimit` = input.pushLimit(`_length`); + inner = Inner.parseFrom(input); + input.popLimit(`_oldLimit`); } + else { input.skipField(tag) } + } + return new Outer(name, inner) + } +} \ No newline at end of file diff --git a/testers/grpc/scala/generated-and-checked-in/com/example/grpc/PaymentMethod.scala b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/PaymentMethod.scala new file mode 100644 index 0000000000..e204a3f7e2 --- /dev/null +++ b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/PaymentMethod.scala @@ -0,0 +1,102 @@ +package com.example.grpc + +import com.example.grpc.PaymentMethodMethod.BankTransferValue +import com.example.grpc.PaymentMethodMethod.CreditCardValue +import com.example.grpc.PaymentMethodMethod.WalletValue +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +case class PaymentMethod( + id: String, + method: PaymentMethodMethod +) { + @throws[IOException] + def writeTo(output: CodedOutputStream): Unit = { + output.writeString(1, this.id) + this.method match { + case null => {} + case c: CreditCardValue => { + output.writeTag(2, 2); + output.writeUInt32NoTag(c.creditCard.getSerializedSize); + c.creditCard.writeTo(output); + } + case c: BankTransferValue => { + output.writeTag(3, 2); + output.writeUInt32NoTag(c.bankTransfer.getSerializedSize); + c.bankTransfer.writeTo(output); + } + case c: WalletValue => { + output.writeTag(4, 2); + output.writeUInt32NoTag(c.wallet.getSerializedSize); + c.wallet.writeTo(output); + } + } + } + + def getSerializedSize: Int = { + var size: Int = 0 + size = size + CodedOutputStream.computeStringSize(1, this.id) + this.method match { + case null => {} + case c: CreditCardValue => size = size + CodedOutputStream.computeTagSize(2) + CodedOutputStream.computeUInt32SizeNoTag(c.creditCard.getSerializedSize) + c.creditCard.getSerializedSize + case c: BankTransferValue => size = size + CodedOutputStream.computeTagSize(3) + CodedOutputStream.computeUInt32SizeNoTag(c.bankTransfer.getSerializedSize) + c.bankTransfer.getSerializedSize + case c: WalletValue => size = size + CodedOutputStream.computeTagSize(4) + CodedOutputStream.computeUInt32SizeNoTag(c.wallet.getSerializedSize) + c.wallet.getSerializedSize + } + return size + } +} + +object PaymentMethod { + given marshaller: Marshaller[PaymentMethod] = { + new Marshaller[PaymentMethod] { + override def stream(value: PaymentMethod): InputStream = { + val bytes = Array.ofDim[Byte](value.getSerializedSize) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch { + case e: IOException => throw new RuntimeException(e) + } + return new ByteArrayInputStream(bytes) + } + override def parse(stream: InputStream): PaymentMethod = { + try { + return PaymentMethod.parseFrom(CodedInputStream.newInstance(stream)) + } catch { + case e: IOException => throw new RuntimeException(e) + } + } + } + } + + @throws[IOException] + def parseFrom(input: CodedInputStream): PaymentMethod = { + var id: String = "" + var method: PaymentMethodMethod = null + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { id = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 2) { val `_length` = input.readRawVarint32(); + val `_oldLimit` = input.pushLimit(`_length`); + method = new CreditCardValue(CreditCard.parseFrom(input)); + input.popLimit(`_oldLimit`); } + else if (WireFormat.getTagFieldNumber(tag) == 3) { val `_length` = input.readRawVarint32(); + val `_oldLimit` = input.pushLimit(`_length`); + method = new BankTransferValue(BankTransfer.parseFrom(input)); + input.popLimit(`_oldLimit`); } + else if (WireFormat.getTagFieldNumber(tag) == 4) { val `_length` = input.readRawVarint32(); + val `_oldLimit` = input.pushLimit(`_length`); + method = new WalletValue(Wallet.parseFrom(input)); + input.popLimit(`_oldLimit`); } + else { input.skipField(tag) } + } + return new PaymentMethod(id, method) + } +} \ No newline at end of file diff --git a/testers/grpc/scala/generated-and-checked-in/com/example/grpc/PaymentMethodMethod.scala b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/PaymentMethodMethod.scala new file mode 100644 index 0000000000..c54316326b --- /dev/null +++ b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/PaymentMethodMethod.scala @@ -0,0 +1,14 @@ +package com.example.grpc + + + +/** OneOf type for method */ +sealed trait PaymentMethodMethod + +object PaymentMethodMethod { + case class BankTransferValue(bankTransfer: BankTransfer) extends PaymentMethodMethod + + case class CreditCardValue(creditCard: CreditCard) extends PaymentMethodMethod + + case class WalletValue(wallet: Wallet) extends PaymentMethodMethod +} \ No newline at end of file diff --git a/testers/grpc/scala/generated-and-checked-in/com/example/grpc/Priority.scala b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/Priority.scala new file mode 100644 index 0000000000..f94071cf94 --- /dev/null +++ b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/Priority.scala @@ -0,0 +1,34 @@ +package com.example.grpc + +import java.lang.IllegalArgumentException + + +enum Priority { + case PRIORITY_UNSPECIFIED, PRIORITY_LOW, PRIORITY_MEDIUM, PRIORITY_HIGH, PRIORITY_CRITICAL + def toValue: Int = { + if (this.toString.equals("PRIORITY_UNSPECIFIED")) { return 0 } + else if (this.toString.equals("PRIORITY_LOW")) { return 1 } + else if (this.toString.equals("PRIORITY_MEDIUM")) { return 2 } + else if (this.toString.equals("PRIORITY_HIGH")) { return 3 } + else if (this.toString.equals("PRIORITY_CRITICAL")) { return 4 } + else { return 0 } + } +} + +object Priority { + def fromValue(value: Int): Priority = { + if (value == 0) { return Priority.PRIORITY_UNSPECIFIED } + else if (value == 1) { return Priority.PRIORITY_LOW } + else if (value == 2) { return Priority.PRIORITY_MEDIUM } + else if (value == 3) { return Priority.PRIORITY_HIGH } + else if (value == 4) { return Priority.PRIORITY_CRITICAL } + else { throw new IllegalArgumentException("Unknown enum value: " + value) } + } + extension (e: Priority) def value: java.lang.String = e.toString + def apply(str: java.lang.String): scala.Either[java.lang.String, Priority] = + scala.util.Try(Priority.valueOf(str)).toEither.left.map(_ => s"'$str' does not match any of the following legal values: $Names") + def force(str: java.lang.String): Priority = Priority.valueOf(str) + val All: scala.List[Priority] = values.toList + val Names: java.lang.String = All.map(_.toString).mkString(", ") + val ByName: scala.collection.immutable.Map[java.lang.String, Priority] = All.map(x => (x.toString, x)).toMap +} \ No newline at end of file diff --git a/testers/grpc/scala/generated-and-checked-in/com/example/grpc/ScalarTypes.scala b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/ScalarTypes.scala new file mode 100644 index 0000000000..b0fbb8cd0b --- /dev/null +++ b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/ScalarTypes.scala @@ -0,0 +1,148 @@ +package com.example.grpc + +import com.google.protobuf.ByteString +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +case class ScalarTypes( + doubleVal: Double, + floatVal: Float, + int32Val: Int, + int64Val: Long, + uint32Val: Int, + uint64Val: Long, + sint32Val: Int, + sint64Val: Long, + fixed32Val: Int, + fixed64Val: Long, + sfixed32Val: Int, + sfixed64Val: Long, + boolVal: Boolean, + stringVal: String, + bytesVal: ByteString +) { + @throws[IOException] + def writeTo(output: CodedOutputStream): Unit = { + output.writeDouble(1, this.doubleVal) + output.writeFloat(2, this.floatVal) + output.writeInt32(3, this.int32Val) + output.writeInt64(4, this.int64Val) + output.writeUInt32(5, this.uint32Val) + output.writeUInt64(6, this.uint64Val) + output.writeSInt32(7, this.sint32Val) + output.writeSInt64(8, this.sint64Val) + output.writeFixed32(9, this.fixed32Val) + output.writeFixed64(10, this.fixed64Val) + output.writeSFixed32(11, this.sfixed32Val) + output.writeSFixed64(12, this.sfixed64Val) + output.writeBool(13, this.boolVal) + output.writeString(14, this.stringVal) + output.writeBytes(15, this.bytesVal) + } + + def getSerializedSize: Int = { + var size: Int = 0 + size = size + CodedOutputStream.computeDoubleSize(1, this.doubleVal) + size = size + CodedOutputStream.computeFloatSize(2, this.floatVal) + size = size + CodedOutputStream.computeInt32Size(3, this.int32Val) + size = size + CodedOutputStream.computeInt64Size(4, this.int64Val) + size = size + CodedOutputStream.computeUInt32Size(5, this.uint32Val) + size = size + CodedOutputStream.computeUInt64Size(6, this.uint64Val) + size = size + CodedOutputStream.computeSInt32Size(7, this.sint32Val) + size = size + CodedOutputStream.computeSInt64Size(8, this.sint64Val) + size = size + CodedOutputStream.computeFixed32Size(9, this.fixed32Val) + size = size + CodedOutputStream.computeFixed64Size(10, this.fixed64Val) + size = size + CodedOutputStream.computeSFixed32Size(11, this.sfixed32Val) + size = size + CodedOutputStream.computeSFixed64Size(12, this.sfixed64Val) + size = size + CodedOutputStream.computeBoolSize(13, this.boolVal) + size = size + CodedOutputStream.computeStringSize(14, this.stringVal) + size = size + CodedOutputStream.computeBytesSize(15, this.bytesVal) + return size + } +} + +object ScalarTypes { + given marshaller: Marshaller[ScalarTypes] = { + new Marshaller[ScalarTypes] { + override def stream(value: ScalarTypes): InputStream = { + val bytes = Array.ofDim[Byte](value.getSerializedSize) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch { + case e: IOException => throw new RuntimeException(e) + } + return new ByteArrayInputStream(bytes) + } + override def parse(stream: InputStream): ScalarTypes = { + try { + return ScalarTypes.parseFrom(CodedInputStream.newInstance(stream)) + } catch { + case e: IOException => throw new RuntimeException(e) + } + } + } + } + + @throws[IOException] + def parseFrom(input: CodedInputStream): ScalarTypes = { + var doubleVal: Double = 0.0 + var floatVal: Float = 0.0f + var int32Val: Int = 0 + var int64Val: Long = 0L + var uint32Val: Int = 0 + var uint64Val: Long = 0L + var sint32Val: Int = 0 + var sint64Val: Long = 0L + var fixed32Val: Int = 0 + var fixed64Val: Long = 0L + var sfixed32Val: Int = 0 + var sfixed64Val: Long = 0L + var boolVal: Boolean = false + var stringVal: String = "" + var bytesVal: ByteString = ByteString.EMPTY + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { doubleVal = input.readDouble() } + else if (WireFormat.getTagFieldNumber(tag) == 2) { floatVal = input.readFloat() } + else if (WireFormat.getTagFieldNumber(tag) == 3) { int32Val = input.readInt32() } + else if (WireFormat.getTagFieldNumber(tag) == 4) { int64Val = input.readInt64() } + else if (WireFormat.getTagFieldNumber(tag) == 5) { uint32Val = input.readUInt32() } + else if (WireFormat.getTagFieldNumber(tag) == 6) { uint64Val = input.readUInt64() } + else if (WireFormat.getTagFieldNumber(tag) == 7) { sint32Val = input.readSInt32() } + else if (WireFormat.getTagFieldNumber(tag) == 8) { sint64Val = input.readSInt64() } + else if (WireFormat.getTagFieldNumber(tag) == 9) { fixed32Val = input.readFixed32() } + else if (WireFormat.getTagFieldNumber(tag) == 10) { fixed64Val = input.readFixed64() } + else if (WireFormat.getTagFieldNumber(tag) == 11) { sfixed32Val = input.readSFixed32() } + else if (WireFormat.getTagFieldNumber(tag) == 12) { sfixed64Val = input.readSFixed64() } + else if (WireFormat.getTagFieldNumber(tag) == 13) { boolVal = input.readBool() } + else if (WireFormat.getTagFieldNumber(tag) == 14) { stringVal = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 15) { bytesVal = input.readBytes() } + else { input.skipField(tag) } + } + return new ScalarTypes( + doubleVal, + floatVal, + int32Val, + int64Val, + uint32Val, + uint64Val, + sint32Val, + sint64Val, + fixed32Val, + fixed64Val, + sfixed32Val, + sfixed64Val, + boolVal, + stringVal, + bytesVal + ) + } +} \ No newline at end of file diff --git a/testers/grpc/scala/generated-and-checked-in/com/example/grpc/Wallet.scala b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/Wallet.scala new file mode 100644 index 0000000000..d79526faab --- /dev/null +++ b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/Wallet.scala @@ -0,0 +1,66 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException + +case class Wallet( + walletId: String, + provider: String +) { + @throws[IOException] + def writeTo(output: CodedOutputStream): Unit = { + output.writeString(1, this.walletId) + output.writeString(2, this.provider) + } + + def getSerializedSize: Int = { + var size: Int = 0 + size = size + CodedOutputStream.computeStringSize(1, this.walletId) + size = size + CodedOutputStream.computeStringSize(2, this.provider) + return size + } +} + +object Wallet { + given marshaller: Marshaller[Wallet] = { + new Marshaller[Wallet] { + override def stream(value: Wallet): InputStream = { + val bytes = Array.ofDim[Byte](value.getSerializedSize) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch { + case e: IOException => throw new RuntimeException(e) + } + return new ByteArrayInputStream(bytes) + } + override def parse(stream: InputStream): Wallet = { + try { + return Wallet.parseFrom(CodedInputStream.newInstance(stream)) + } catch { + case e: IOException => throw new RuntimeException(e) + } + } + } + } + + @throws[IOException] + def parseFrom(input: CodedInputStream): Wallet = { + var walletId: String = "" + var provider: String = "" + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { walletId = input.readString() } + else if (WireFormat.getTagFieldNumber(tag) == 2) { provider = input.readString() } + else { input.skipField(tag) } + } + return new Wallet(walletId, provider) + } +} \ No newline at end of file diff --git a/testers/grpc/scala/generated-and-checked-in/com/example/grpc/WellKnownTypesMessage.scala b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/WellKnownTypesMessage.scala new file mode 100644 index 0000000000..9615b53046 --- /dev/null +++ b/testers/grpc/scala/generated-and-checked-in/com/example/grpc/WellKnownTypesMessage.scala @@ -0,0 +1,153 @@ +package com.example.grpc + +import com.google.protobuf.CodedInputStream +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.WireFormat +import io.grpc.MethodDescriptor.Marshaller +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import java.lang.RuntimeException +import java.time.Duration +import java.time.Instant + +case class WellKnownTypesMessage( + createdAt: Instant, + ttl: Duration, + nullableString: Option[String], + nullableInt: Option[Int], + nullableBool: Option[Boolean] +) { + @throws[IOException] + def writeTo(output: CodedOutputStream): Unit = { + output.writeTag(1, 2) + output.writeUInt32NoTag(CodedOutputStream.computeInt64Size(1, this.createdAt.getEpochSecond()) + CodedOutputStream.computeInt32Size(2, this.createdAt.getNano())) + output.writeInt64(1, this.createdAt.getEpochSecond()) + output.writeInt32(2, this.createdAt.getNano()) + output.writeTag(2, 2) + output.writeUInt32NoTag(CodedOutputStream.computeInt64Size(1, this.ttl.getSeconds()) + CodedOutputStream.computeInt32Size(2, this.ttl.getNano())) + output.writeInt64(1, this.ttl.getSeconds()) + output.writeInt32(2, this.ttl.getNano()) + if (this.nullableString.isDefined) { + val v = this.nullableString.get; + output.writeTag(3, 2); + output.writeUInt32NoTag(CodedOutputStream.computeStringSize(1, v)); + output.writeString(1, v); + } + if (this.nullableInt.isDefined) { + val v = this.nullableInt.get; + output.writeTag(4, 2); + output.writeUInt32NoTag(CodedOutputStream.computeInt32Size(1, v)); + output.writeInt32(1, v); + } + if (this.nullableBool.isDefined) { + val v = this.nullableBool.get; + output.writeTag(5, 2); + output.writeUInt32NoTag(CodedOutputStream.computeBoolSize(1, v)); + output.writeBool(1, v); + } + } + + def getSerializedSize: Int = { + var size: Int = 0 + size = size + CodedOutputStream.computeTagSize(1) + CodedOutputStream.computeUInt32SizeNoTag(CodedOutputStream.computeInt64Size(1, this.createdAt.getEpochSecond()) + CodedOutputStream.computeInt32Size(2, this.createdAt.getNano())) + CodedOutputStream.computeInt64Size(1, this.createdAt.getEpochSecond()) + CodedOutputStream.computeInt32Size(2, this.createdAt.getNano()) + size = size + CodedOutputStream.computeTagSize(2) + CodedOutputStream.computeUInt32SizeNoTag(CodedOutputStream.computeInt64Size(1, this.ttl.getSeconds()) + CodedOutputStream.computeInt32Size(2, this.ttl.getNano())) + CodedOutputStream.computeInt64Size(1, this.ttl.getSeconds()) + CodedOutputStream.computeInt32Size(2, this.ttl.getNano()) + if (this.nullableString.isDefined) { + val v = this.nullableString.get; + size = size + CodedOutputStream.computeTagSize(3) + CodedOutputStream.computeUInt32SizeNoTag(CodedOutputStream.computeStringSize(1, v)) + CodedOutputStream.computeStringSize(1, v); + } + if (this.nullableInt.isDefined) { + val v = this.nullableInt.get; + size = size + CodedOutputStream.computeTagSize(4) + CodedOutputStream.computeUInt32SizeNoTag(CodedOutputStream.computeInt32Size(1, v)) + CodedOutputStream.computeInt32Size(1, v); + } + if (this.nullableBool.isDefined) { + val v = this.nullableBool.get; + size = size + CodedOutputStream.computeTagSize(5) + CodedOutputStream.computeUInt32SizeNoTag(CodedOutputStream.computeBoolSize(1, v)) + CodedOutputStream.computeBoolSize(1, v); + } + return size + } +} + +object WellKnownTypesMessage { + given marshaller: Marshaller[WellKnownTypesMessage] = { + new Marshaller[WellKnownTypesMessage] { + override def stream(value: WellKnownTypesMessage): InputStream = { + val bytes = Array.ofDim[Byte](value.getSerializedSize) + val cos = CodedOutputStream.newInstance(bytes) + try { + value.writeTo(cos) + cos.flush() + } catch { + case e: IOException => throw new RuntimeException(e) + } + return new ByteArrayInputStream(bytes) + } + override def parse(stream: InputStream): WellKnownTypesMessage = { + try { + return WellKnownTypesMessage.parseFrom(CodedInputStream.newInstance(stream)) + } catch { + case e: IOException => throw new RuntimeException(e) + } + } + } + } + + @throws[IOException] + def parseFrom(input: CodedInputStream): WellKnownTypesMessage = { + var createdAt: Instant = Instant.EPOCH + var ttl: Duration = Duration.ZERO + var nullableString: Option[String] = None + var nullableInt: Option[Int] = None + var nullableBool: Option[Boolean] = None + while (!input.isAtEnd()) { + val tag = input.readTag() + if (WireFormat.getTagFieldNumber(tag) == 1) { val `_length` = input.readRawVarint32(); + val `_oldLimit` = input.pushLimit(`_length`); + var `_tsSeconds` = 0L; + var `_tsNanos` = 0; + while (!input.isAtEnd()) { + val `_tsTag` = input.readTag() + if (WireFormat.getTagFieldNumber(`_tsTag`) == 1) { `_tsSeconds` = input.readInt64() } + else if (WireFormat.getTagFieldNumber(`_tsTag`) == 2) { `_tsNanos` = input.readInt32() } + else { input.skipField(`_tsTag`) } + }; + createdAt = Instant.ofEpochSecond(`_tsSeconds`, `_tsNanos`.toLong); + input.popLimit(`_oldLimit`); } + else if (WireFormat.getTagFieldNumber(tag) == 2) { val `_length` = input.readRawVarint32(); + val `_oldLimit` = input.pushLimit(`_length`); + var `_durSeconds` = 0L; + var `_durNanos` = 0; + while (!input.isAtEnd()) { + val `_durTag` = input.readTag() + if (WireFormat.getTagFieldNumber(`_durTag`) == 1) { `_durSeconds` = input.readInt64() } + else if (WireFormat.getTagFieldNumber(`_durTag`) == 2) { `_durNanos` = input.readInt32() } + else { input.skipField(`_durTag`) } + }; + ttl = Duration.ofSeconds(`_durSeconds`, `_durNanos`.toLong); + input.popLimit(`_oldLimit`); } + else if (WireFormat.getTagFieldNumber(tag) == 3) { val `_length` = input.readRawVarint32(); + val `_oldLimit` = input.pushLimit(`_length`); + input.readTag(); + nullableString = Some(input.readString()); + input.popLimit(`_oldLimit`); } + else if (WireFormat.getTagFieldNumber(tag) == 4) { val `_length` = input.readRawVarint32(); + val `_oldLimit` = input.pushLimit(`_length`); + input.readTag(); + nullableInt = Some(input.readInt32()); + input.popLimit(`_oldLimit`); } + else if (WireFormat.getTagFieldNumber(tag) == 5) { val `_length` = input.readRawVarint32(); + val `_oldLimit` = input.pushLimit(`_length`); + input.readTag(); + nullableBool = Some(input.readBool()); + input.popLimit(`_oldLimit`); } + else { input.skipField(tag) } + } + return new WellKnownTypesMessage( + createdAt, + ttl, + nullableString, + nullableInt, + nullableBool + ) + } +} \ No newline at end of file diff --git a/testers/grpc/scala/src/scala/com/example/grpc/GrpcIntegrationTest.scala b/testers/grpc/scala/src/scala/com/example/grpc/GrpcIntegrationTest.scala new file mode 100644 index 0000000000..a094c089e8 --- /dev/null +++ b/testers/grpc/scala/src/scala/com/example/grpc/GrpcIntegrationTest.scala @@ -0,0 +1,454 @@ +package com.example.grpc + +import com.google.protobuf.ByteString +import io.grpc.ManagedChannel +import io.grpc.Server +import io.grpc.inprocess.InProcessChannelBuilder +import io.grpc.inprocess.InProcessServerBuilder +import org.junit.After +import org.junit.Assert.* +import org.junit.Before +import org.junit.Test + +import java.time.Duration +import java.time.Instant +import java.util + +class GrpcIntegrationTest { + + private var server: Server = _ + private var channel: ManagedChannel = _ + private var orderClient: OrderServiceClient = _ + private var echoClient: EchoServiceClient = _ + + private val testCustomer = + Customer(CustomerId.valueOf("CUST-123"), "John Doe", "john@example.com") + + @Before + def setUp(): Unit = { + val serverName = InProcessServerBuilder.generateName() + + val orderImpl = new OrderService { + override def getCustomer(request: GetCustomerRequest): GetCustomerResponse = + GetCustomerResponse( + Customer(CustomerId.valueOf(request.customerId), "John Doe", "john@example.com") + ) + + override def createOrder(request: CreateOrderRequest): CreateOrderResponse = + CreateOrderResponse(request.order.orderId.unwrap, OrderStatus.ORDER_STATUS_PENDING) + + override def listOrders(request: ListOrdersRequest): util.Iterator[OrderUpdate] = { + val updates = new util.ArrayList[OrderUpdate]() + updates.add(OrderUpdate("ORD-1", OrderStatus.ORDER_STATUS_PENDING, Instant.ofEpochSecond(1000, 500))) + updates.add(OrderUpdate("ORD-2", OrderStatus.ORDER_STATUS_SHIPPED, Instant.ofEpochSecond(2000, 1000))) + updates.add(OrderUpdate("ORD-3", OrderStatus.ORDER_STATUS_DELIVERED, Instant.ofEpochSecond(3000, 0))) + updates.iterator() + } + + override def submitOrders(requests: util.Iterator[CreateOrderRequest]): OrderSummary = + throw new UnsupportedOperationException() + + override def chat(requests: util.Iterator[ChatMessage]): util.Iterator[ChatMessage] = + throw new UnsupportedOperationException() + } + + val echoImpl = new EchoService { + override def echoScalarTypes(request: ScalarTypes): ScalarTypes = request + override def echoCustomer(request: Customer): Customer = request + override def echoOrder(request: Order): Order = request + override def echoInventory(request: Inventory): Inventory = request + override def echoOuter(request: Outer): Outer = request + override def echoOptionalFields(request: OptionalFields): OptionalFields = request + override def echoWellKnownTypes(request: WellKnownTypesMessage): WellKnownTypesMessage = request + override def echoPaymentMethod(request: PaymentMethod): PaymentMethod = request + override def echoNotification(request: Notification): Notification = request + } + + server = InProcessServerBuilder + .forName(serverName) + .directExecutor() + .addService(new OrderServiceServer(orderImpl)) + .addService(new EchoServiceServer(echoImpl)) + .build() + .start() + + channel = InProcessChannelBuilder.forName(serverName).directExecutor().build() + orderClient = new OrderServiceClient(channel) + echoClient = new EchoServiceClient(channel) + } + + @After + def tearDown(): Unit = { + channel.shutdownNow() + server.shutdownNow() + } + + // ---- gRPC service tests ---- + + @Test + def testGetCustomer(): Unit = { + val response = orderClient.getCustomer(GetCustomerRequest("CUST-123")) + assertNotNull(response) + assertNotNull(response.customer) + assertEquals("CUST-123", response.customer.customerId.unwrap) + assertEquals("John Doe", response.customer.name) + assertEquals("john@example.com", response.customer.email) + } + + @Test + def testCreateOrder(): Unit = { + val order = Order( + OrderId.valueOf("ORD-42"), + CustomerId.valueOf("CUST-1"), + 9999L, + Instant.ofEpochSecond(1700000000L, 123456789) + ) + + val response = orderClient.createOrder(CreateOrderRequest(order)) + assertNotNull(response) + assertEquals("ORD-42", response.orderId) + assertEquals(OrderStatus.ORDER_STATUS_PENDING, response.status) + } + + @Test + def testListOrders(): Unit = { + val updates = orderClient.listOrders(ListOrdersRequest("CUST-123", 10)) + val results = new util.ArrayList[OrderUpdate]() + updates.forEachRemaining(results.add(_)) + + assertEquals(3, results.size()) + assertEquals("ORD-1", results.get(0).orderId) + assertEquals(OrderStatus.ORDER_STATUS_PENDING, results.get(0).status) + assertEquals("ORD-2", results.get(1).orderId) + assertEquals(OrderStatus.ORDER_STATUS_SHIPPED, results.get(1).status) + assertEquals("ORD-3", results.get(2).orderId) + assertEquals(OrderStatus.ORDER_STATUS_DELIVERED, results.get(2).status) + } + + // ---- Echo round-trip tests ---- + + @Test + def testEchoCustomer(): Unit = { + val parsed = echoClient.echoCustomer(testCustomer) + assertEquals(testCustomer, parsed) + } + + @Test + def testEchoOrder(): Unit = { + val order = Order( + OrderId.valueOf("ORD-1"), + CustomerId.valueOf("CUST-1"), + 5000L, + Instant.ofEpochSecond(1700000000L, 123456789) + ) + val parsed = echoClient.echoOrder(order) + assertEquals(order.orderId, parsed.orderId) + assertEquals(order.customerId, parsed.customerId) + assertEquals(order.amountCents, parsed.amountCents) + assertEquals(order.createdAt, parsed.createdAt) + } + + // ---- Scalar types ---- + + @Test + def testEchoScalarTypes(): Unit = { + val scalars = ScalarTypes( + 3.14, + 2.71f, + 42, + 9876543210L, + 100, + 200L, + -50, + -100L, + 999, + 888L, + -777, + -666L, + true, + "hello world", + ByteString.copyFromUtf8("binary data") + ) + val parsed = echoClient.echoScalarTypes(scalars) + assertEquals(scalars.doubleVal, parsed.doubleVal, 0.0001) + assertEquals(scalars.floatVal, parsed.floatVal, 0.0001f) + assertEquals(scalars.int32Val, parsed.int32Val) + assertEquals(scalars.int64Val, parsed.int64Val) + assertEquals(scalars.uint32Val, parsed.uint32Val) + assertEquals(scalars.uint64Val, parsed.uint64Val) + assertEquals(scalars.sint32Val, parsed.sint32Val) + assertEquals(scalars.sint64Val, parsed.sint64Val) + assertEquals(scalars.fixed32Val, parsed.fixed32Val) + assertEquals(scalars.fixed64Val, parsed.fixed64Val) + assertEquals(scalars.sfixed32Val, parsed.sfixed32Val) + assertEquals(scalars.sfixed64Val, parsed.sfixed64Val) + assertEquals(scalars.boolVal, parsed.boolVal) + assertEquals(scalars.stringVal, parsed.stringVal) + assertEquals(scalars.bytesVal, parsed.bytesVal) + } + + // ---- Enum tests ---- + + @Test + def testEnumToValueFromValueRoundTrip(): Unit = { + for (status <- OrderStatus.values) { + val wireValue = status.toValue + val back = OrderStatus.fromValue(wireValue) + assertEquals(status, back) + } + } + + @Test + def testEnumForce(): Unit = { + assertEquals(OrderStatus.ORDER_STATUS_PENDING, OrderStatus.force("ORDER_STATUS_PENDING")) + } + + @Test(expected = classOf[RuntimeException]) + def testEnumForceInvalid(): Unit = { + OrderStatus.force("NONEXISTENT") + } + + @Test(expected = classOf[IllegalArgumentException]) + def testEnumFromValueInvalid(): Unit = { + OrderStatus.fromValue(999) + } + + @Test + def testPriorityEnumRoundTrip(): Unit = { + for (p <- Priority.values) { + val wireValue = p.toValue + val back = Priority.fromValue(wireValue) + assertEquals(p, back) + } + } + + // ---- Optional fields ---- + + @Test + def testEchoOptionalFieldsAllPresent(): Unit = { + val opt = OptionalFields(Some("Alice"), Some(30), Some(testCustomer)) + val parsed = echoClient.echoOptionalFields(opt) + assertEquals(Some("Alice"), parsed.name) + assertEquals(Some(30), parsed.age) + assertTrue(parsed.customer.isDefined) + assertEquals("CUST-123", parsed.customer.get.customerId.unwrap) + } + + @Test + def testEchoOptionalFieldsAllEmpty(): Unit = { + val opt = OptionalFields(None, None, None) + val parsed = echoClient.echoOptionalFields(opt) + assertEquals(None, parsed.name) + assertEquals(None, parsed.age) + assertEquals(None, parsed.customer) + } + + @Test + def testEchoOptionalFieldsPartiallyPresent(): Unit = { + val opt = OptionalFields(Some("Bob"), None, None) + val parsed = echoClient.echoOptionalFields(opt) + assertEquals(Some("Bob"), parsed.name) + assertEquals(None, parsed.age) + assertEquals(None, parsed.customer) + } + + // ---- Nested messages ---- + + @Test + def testEchoOuter(): Unit = { + val outer = Outer("outer-name", Inner(42, "inner-desc")) + val parsed = echoClient.echoOuter(outer) + assertEquals("outer-name", parsed.name) + assertNotNull(parsed.inner) + assertEquals(42, parsed.inner.value) + assertEquals("inner-desc", parsed.inner.description) + } + + // ---- OneOf types ---- + + @Test + def testEchoPaymentMethodCreditCard(): Unit = { + val cc = CreditCard("4111111111111111", "12/25", "123") + val method = PaymentMethodMethod.CreditCardValue(cc) + val pm = PaymentMethod("PAY-1", method) + val parsed = echoClient.echoPaymentMethod(pm) + assertEquals("PAY-1", parsed.id) + parsed.method match { + case PaymentMethodMethod.CreditCardValue(creditCard) => + assertEquals("4111111111111111", creditCard.cardNumber) + assertEquals("12/25", creditCard.expiryDate) + assertEquals("123", creditCard.cvv) + case other => fail(s"Expected CreditCardValue, got $other") + } + } + + @Test + def testEchoPaymentMethodBankTransfer(): Unit = { + val bt = BankTransfer("123456789", "021000021") + val method = PaymentMethodMethod.BankTransferValue(bt) + val pm = PaymentMethod("PAY-2", method) + val parsed = echoClient.echoPaymentMethod(pm) + assertEquals("PAY-2", parsed.id) + parsed.method match { + case PaymentMethodMethod.BankTransferValue(bankTransfer) => + assertEquals("123456789", bankTransfer.accountNumber) + assertEquals("021000021", bankTransfer.routingNumber) + case other => fail(s"Expected BankTransferValue, got $other") + } + } + + @Test + def testEchoPaymentMethodWallet(): Unit = { + val w = Wallet("wallet-42", "Stripe") + val method = PaymentMethodMethod.WalletValue(w) + val pm = PaymentMethod("PAY-3", method) + val parsed = echoClient.echoPaymentMethod(pm) + assertEquals("PAY-3", parsed.id) + parsed.method match { + case PaymentMethodMethod.WalletValue(wallet) => + assertEquals("wallet-42", wallet.walletId) + assertEquals("Stripe", wallet.provider) + case other => fail(s"Expected WalletValue, got $other") + } + } + + @Test + def testEchoNotificationWithEmailTarget(): Unit = { + val notif = Notification("Hello!", Priority.PRIORITY_HIGH, NotificationTarget.Email("user@example.com")) + val parsed = echoClient.echoNotification(notif) + assertEquals("Hello!", parsed.message) + assertEquals(Priority.PRIORITY_HIGH, parsed.priority) + parsed.target match { + case NotificationTarget.Email(email) => assertEquals("user@example.com", email) + case other => fail(s"Expected Email, got $other") + } + } + + @Test + def testEchoNotificationWithPhoneTarget(): Unit = { + val notif = Notification("Alert", Priority.PRIORITY_CRITICAL, NotificationTarget.Phone("+1234567890")) + val parsed = echoClient.echoNotification(notif) + assertEquals("Alert", parsed.message) + assertEquals(Priority.PRIORITY_CRITICAL, parsed.priority) + parsed.target match { + case NotificationTarget.Phone(phone) => assertEquals("+1234567890", phone) + case other => fail(s"Expected Phone, got $other") + } + } + + @Test + def testEchoNotificationWithWebhookTarget(): Unit = { + val notif = Notification("Event", Priority.PRIORITY_LOW, NotificationTarget.WebhookUrl("https://hooks.example.com/abc")) + val parsed = echoClient.echoNotification(notif) + assertEquals("Event", parsed.message) + assertEquals(Priority.PRIORITY_LOW, parsed.priority) + parsed.target match { + case NotificationTarget.WebhookUrl(webhookUrl) => assertEquals("https://hooks.example.com/abc", webhookUrl) + case other => fail(s"Expected WebhookUrl, got $other") + } + } + + // ---- Collections ---- + + @Test + def testEchoInventory(): Unit = { + val productIds = List("PROD-1", "PROD-2", "PROD-3") + val stockCounts = Map("PROD-1" -> 100, "PROD-2" -> 200, "PROD-3" -> 0) + val orders = List( + Order(OrderId.valueOf("ORD-1"), CustomerId.valueOf("CUST-1"), 1000L, Instant.ofEpochSecond(1000, 0)), + Order(OrderId.valueOf("ORD-2"), CustomerId.valueOf("CUST-2"), 2000L, Instant.ofEpochSecond(2000, 0)) + ) + + val inventory = Inventory("WH-1", productIds, stockCounts, orders) + val parsed = echoClient.echoInventory(inventory) + assertEquals("WH-1", parsed.warehouseId) + assertEquals(3, parsed.productIds.size) + assertEquals("PROD-1", parsed.productIds(0)) + assertEquals("PROD-2", parsed.productIds(1)) + assertEquals("PROD-3", parsed.productIds(2)) + assertEquals(3, parsed.stockCounts.size) + assertEquals(100, parsed.stockCounts("PROD-1")) + assertEquals(200, parsed.stockCounts("PROD-2")) + assertEquals(0, parsed.stockCounts("PROD-3")) + assertEquals(2, parsed.recentOrders.size) + assertEquals("ORD-1", parsed.recentOrders(0).orderId.unwrap) + assertEquals("ORD-2", parsed.recentOrders(1).orderId.unwrap) + } + + @Test + def testEchoInventoryEmptyCollections(): Unit = { + val inventory = Inventory("WH-EMPTY", List.empty, Map.empty, List.empty) + val parsed = echoClient.echoInventory(inventory) + assertEquals("WH-EMPTY", parsed.warehouseId) + assertTrue(parsed.productIds.isEmpty) + assertTrue(parsed.stockCounts.isEmpty) + assertTrue(parsed.recentOrders.isEmpty) + } + + // ---- Well-known types ---- + + @Test + def testEchoWellKnownTypes(): Unit = { + val msg = WellKnownTypesMessage( + Instant.ofEpochSecond(1700000000L, 123456789), + Duration.ofSeconds(3600, 500000000), + Some("hello"), + Some(42), + Some(true) + ) + val parsed = echoClient.echoWellKnownTypes(msg) + assertEquals(msg.createdAt, parsed.createdAt) + assertEquals(msg.ttl, parsed.ttl) + assertEquals(Some("hello"), parsed.nullableString) + assertEquals(Some(42), parsed.nullableInt) + assertEquals(Some(true), parsed.nullableBool) + } + + // ---- Wrapper ID types ---- + + @Test + def testCustomerIdValueOf(): Unit = { + val id = CustomerId.valueOf("abc") + assertEquals("abc", id.unwrap) + } + + @Test + def testOrderIdValueOf(): Unit = { + val id = OrderId.valueOf("ORD-1") + assertEquals("ORD-1", id.unwrap) + } + + // ---- With methods (copy) ---- + + @Test + def testCustomerWithMethods(): Unit = { + val updated = testCustomer.copy(name = "Jane Doe") + assertEquals("Jane Doe", updated.name) + assertEquals(testCustomer.customerId, updated.customerId) + assertEquals(testCustomer.email, updated.email) + } + + @Test + def testOrderWithMethods(): Unit = { + val order = Order( + OrderId.valueOf("ORD-1"), + CustomerId.valueOf("CUST-1"), + 1000L, + Instant.ofEpochSecond(1000, 0) + ) + val updated = order.copy(amountCents = 2000L) + assertEquals(2000L, updated.amountCents) + assertEquals(order.orderId, updated.orderId) + } + + // ---- Echo with empty strings ---- + + @Test + def testEchoCustomerEmptyStrings(): Unit = { + val empty = Customer(CustomerId.valueOf(""), "", "") + val parsed = echoClient.echoCustomer(empty) + assertEquals("", parsed.customerId.unwrap) + assertEquals("", parsed.name) + assertEquals("", parsed.email) + } +} diff --git a/typr-scripts/src/scala/scripts/GenerateGrpcTest.scala b/typr-scripts/src/scala/scripts/GenerateGrpcTest.scala new file mode 100644 index 0000000000..3cd80a0541 --- /dev/null +++ b/typr-scripts/src/scala/scripts/GenerateGrpcTest.scala @@ -0,0 +1,146 @@ +package scripts + +import typr.grpc.{GrpcCodegen, GrpcOptions, GrpcFrameworkIntegration, ProtoSource} +import typr.effects.EffectType +import typr.internal.FileSync +import typr.jvm +import typr.internal.codegen.{LangJava, LangKotlin, LangScala, TypeSupportKotlin, addPackageAndImports} +import typr.{Dialect, Lang, RelPath, TypeSupportScala} + +import java.nio.file.Path + +object GenerateGrpcTest { + val buildDir: Path = Path.of(sys.props("user.dir")) + + def main(args: Array[String]): Unit = { + val protosPath = buildDir.resolve("testers/grpc/protos") + + println(s"Generating gRPC code from: $protosPath") + + val langScala = LangScala.javaDsl(Dialect.Scala3, TypeSupportScala) + val langKotlin = LangKotlin(TypeSupportKotlin) + + // Java - Blocking (default) + generateCode( + protosPath = protosPath, + lang = LangJava, + outputDir = "testers/grpc/java", + effectType = EffectType.Blocking, + frameworkIntegration = GrpcFrameworkIntegration.None + ) + + // Scala - Blocking + generateCode( + protosPath = protosPath, + lang = langScala, + outputDir = "testers/grpc/scala", + effectType = EffectType.Blocking, + frameworkIntegration = GrpcFrameworkIntegration.None + ) + + // Kotlin - Blocking + generateCode( + protosPath = protosPath, + lang = langKotlin, + outputDir = "testers/grpc/kotlin", + effectType = EffectType.Blocking, + frameworkIntegration = GrpcFrameworkIntegration.None + ) + + // Java - Spring framework integration + generateCode( + protosPath = protosPath, + lang = LangJava, + outputDir = "testers/grpc/java-spring", + effectType = EffectType.Blocking, + frameworkIntegration = GrpcFrameworkIntegration.Spring + ) + + // Java - Quarkus framework integration + generateCode( + protosPath = protosPath, + lang = LangJava, + outputDir = "testers/grpc/java-quarkus", + effectType = EffectType.Blocking, + frameworkIntegration = GrpcFrameworkIntegration.Quarkus + ) + + // Kotlin - Quarkus + Mutiny Uni + generateCode( + protosPath = protosPath, + lang = langKotlin, + outputDir = "testers/grpc/kotlin-quarkus", + effectType = EffectType.MutinyUni, + frameworkIntegration = GrpcFrameworkIntegration.Quarkus + ) + + println("Done!") + } + + private def generateCode( + protosPath: Path, + lang: Lang, + outputDir: String, + effectType: EffectType, + frameworkIntegration: GrpcFrameworkIntegration + ): Unit = { + val projectDir = buildDir.resolve(outputDir) + val sourceDir = projectDir.resolve("generated-and-checked-in") + + println(s"Output directory: $sourceDir") + + val options = GrpcOptions + .default( + pkg = jvm.QIdent(List(jvm.Ident("com"), jvm.Ident("example"), jvm.Ident("grpc"))), + protoSource = ProtoSource.Directory(protosPath) + ) + .copy( + effectType = effectType, + frameworkIntegration = frameworkIntegration + ) + + val result = GrpcCodegen.generate(options, lang) + + if (result.errors.nonEmpty) { + println("Errors:") + result.errors.foreach(e => println(s" - $e")) + sys.exit(1) + } + + if (result.files.isEmpty) { + println("No files generated!") + return + } + + // Build known names by package for import resolution + val knownNamesByPkg: Map[jvm.QIdent, Map[jvm.Ident, jvm.Type.Qualified]] = result.files + .groupBy(_.pkg) + .map { case (pkg, files) => + pkg -> files.flatMap { f => + f.secondaryTypes.map(st => st.value.name -> st) :+ (f.tpe.value.name -> f.tpe) + }.toMap + } + + // Convert files to RelPath -> String map for FileSync + val fileMap: Map[RelPath, String] = result.files.map { file => + val pathParts = file.tpe.value.idents.map(_.value) + val relativePath = RelPath(pathParts.init :+ s"${pathParts.last}.${lang.extension}") + val fileWithImports = addPackageAndImports(lang, knownNamesByPkg, file) + relativePath -> fileWithImports.contents.render(lang).asString + }.toMap + + // Use FileSync to write files (delete old files to avoid conflicts) + val synced = FileSync.syncStrings( + folder = sourceDir, + fileRelMap = fileMap, + deleteUnknowns = FileSync.DeleteUnknowns.Yes(maxDepth = None), + softWrite = FileSync.SoftWrite.Yes(Set.empty) + ) + + val changed = synced.filter { case (_, status) => status != FileSync.Synced.Unchanged } + println(s"Generated ${result.files.size} files (${changed.size} changed):") + changed.foreach { case (path, status) => + println(s" - $status: ${sourceDir.relativize(path)}") + } + } +} diff --git a/typr/src/resources/typr/annotations.proto b/typr/src/resources/typr/annotations.proto new file mode 100644 index 0000000000..fd010c0693 --- /dev/null +++ b/typr/src/resources/typr/annotations.proto @@ -0,0 +1,8 @@ +syntax = "proto3"; +package typr; + +import "google/protobuf/descriptor.proto"; + +extend google.protobuf.FieldOptions { + optional string wrapper = 50000; +} diff --git a/typr/src/scala/typr/Lang.scala b/typr/src/scala/typr/Lang.scala index 2d78c7e941..469e0ea6a2 100644 --- a/typr/src/scala/typr/Lang.scala +++ b/typr/src/scala/typr/Lang.scala @@ -150,6 +150,9 @@ trait Lang { /** Cast from Object to a specific type. Java: (Type) expr, Scala: expr.asInstanceOf[Type] */ def castFromObject(targetType: jvm.Type, expr: jvm.Code): jvm.Code + /** Make a reference type nullable. Java/Scala: noop (reference types are nullable). Kotlin: T? */ + def nullableRefType(tpe: jvm.Type): jvm.Type + /** Convenience method for property access on generated data classes */ final def prop(target: jvm.Code, field: jvm.Ident): jvm.Code = propertyGetterAccess(target, field) final def prop(target: jvm.Code, field: String): jvm.Code = propertyGetterAccess(target, jvm.Ident(field)) @@ -180,6 +183,13 @@ trait ListSupport { /** Iterate over collection with a consumer lambda (for side effects) */ def forEach(collection: jvm.Code, lambda: jvm.Code): jvm.Code + /** For-each loop over collection elements with statement body (not a lambda). + * - Java: `for (Type elem : collection) { body }` + * - Scala: `for (elem <- collection) { body }` + * - Kotlin: `for (elem in collection) { body }` + */ + def forEachStmt(collection: jvm.Code, elem: jvm.Ident, elemType: jvm.Type)(body: jvm.Code => List[jvm.Code]): jvm.Code + /** Map over array elements and collect to list. Scala: array.map(mapper).toList, Java: Arrays.stream(array).map(mapper).toList() */ def arrayMapToList(array: jvm.Code, mapper: jvm.Code): jvm.Code @@ -323,4 +333,10 @@ trait MapSupport { * - Kotlin: map[key] */ def getNullable(map: jvm.Code, key: jvm.Code): jvm.Code + + /** Iterate over map entries with key and value references. + * - Java/Kotlin: for (Map.Entry entry : map.entrySet()) { body(entry.getKey(), entry.getValue()) } + * - Scala: for ((key, value) <- map) { body } + */ + def forEachEntry(map: jvm.Code, keyType: jvm.Type, valueType: jvm.Type)(body: (jvm.Code, jvm.Code) => List[jvm.Code]): jvm.Code } diff --git a/typr/src/scala/typr/Naming.scala b/typr/src/scala/typr/Naming.scala index 6da771cf28..5e85d6531f 100644 --- a/typr/src/scala/typr/Naming.scala +++ b/typr/src/scala/typr/Naming.scala @@ -359,6 +359,93 @@ class Naming(val pkg: jvm.QIdent, lang: Lang) { val basePkg = namespace.map(ns => jvm.QIdent(ns)).getOrElse(avroProtocolPackage) jvm.Type.Qualified(basePkg / jvm.Ident("Result")) } + + // ============================================================================ + // gRPC/Protobuf naming methods + // ============================================================================ + + /** Package for gRPC service types */ + def grpcServicePackage: jvm.QIdent = pkg / jvm.Ident("service") + + /** Message class name from protobuf full name. + * + * Places the clean type under the user's configured `pkg`, using the simple message name. + */ + def grpcMessageTypeName(protoFullName: String): jvm.Type.Qualified = { + val parts = protoFullName.split('.') + val messageName = parts.last + jvm.Type.Qualified(pkg / jvm.Ident(messageName)) + } + + /** Enum class name from protobuf full name */ + def grpcEnumTypeName(protoFullName: String): jvm.Type.Qualified = + grpcMessageTypeName(protoFullName) + + /** OneOf sealed interface/trait name. + * + * Uses MessageNameOneofName as a sibling type to avoid Java file/package name clashes. + */ + def grpcOneofTypeName(messageFullName: String, oneofName: String): jvm.Type.Qualified = { + val parts = messageFullName.split('.') + val messageName = parts.last + val oneofSimpleName = Naming.titleCase(oneofName) + jvm.Type.Qualified(pkg / jvm.Ident(messageName + oneofSimpleName)) + } + + /** OneOf case name for a field within a oneof. + * + * If TitleCase(fieldName) would collide with the field's message type simple name, appends "Value" suffix. + */ + def grpcOneofCaseName(field: typr.grpc.ProtoField): jvm.Ident = { + val baseName = Naming.titleCase(field.name) + val fieldTypeSimpleName = field.fieldType match { + case typr.grpc.ProtoType.Message(fullName) => fullName.split('.').last + case _ => "" + } + if (baseName == fieldTypeSimpleName) jvm.Ident(baseName + "Value") + else jvm.Ident(baseName) + } + + /** Field name from protobuf field (convert snake_case to camelCase) */ + def grpcFieldName(protoFieldName: String): jvm.Ident = + jvm.Ident(Naming.camelCase(protoFieldName.split("_"))) + + /** Enum value name */ + def grpcEnumValueName(symbol: String): jvm.Ident = { + val sanitized = symbol + .replace("-", "_") + .replace(".", "_") + if (sanitized.isEmpty) jvm.Ident("UNKNOWN") + else jvm.Ident(sanitized) + } + + /** Service interface name */ + def grpcServiceTypeName(serviceName: String): jvm.Type.Qualified = + jvm.Type.Qualified(pkg / jvm.Ident(serviceName)) + + /** Server adapter class name */ + def grpcServerTypeName(serviceName: String): jvm.Type.Qualified = + jvm.Type.Qualified(pkg / jvm.Ident(serviceName + "Server")) + + /** Client wrapper class name */ + def grpcClientTypeName(serviceName: String): jvm.Type.Qualified = + jvm.Type.Qualified(pkg / jvm.Ident(serviceName + "Client")) + + /** Wrapper type name from (typr.wrapper) */ + def grpcWrapperTypeName(wrapperName: String): jvm.Type.Qualified = + jvm.Type.Qualified(pkg / jvm.Ident(wrapperName)) + + /** Marshaller field/given name. Scala: `marshaller` (given), Java/Kotlin: `MARSHALLER` (static field) */ + def grpcMarshallerName: jvm.Ident = + if (lang.extension == "scala") jvm.Ident("marshaller") + else jvm.Ident("MARSHALLER") + + /** Method name for gRPC method (convert PascalCase to camelCase) */ + def grpcMethodName(protoMethodName: String): jvm.Ident = { + val camelCased = protoMethodName.head.toLower + protoMethodName.tail + jvm.Ident(camelCased) + } + } object Naming { diff --git a/typr/src/scala/typr/TypeSupport.scala b/typr/src/scala/typr/TypeSupport.scala index 03c2293fd5..3233f1a45b 100644 --- a/typr/src/scala/typr/TypeSupport.scala +++ b/typr/src/scala/typr/TypeSupport.scala @@ -17,6 +17,10 @@ trait TypeSupport { val Float: jvm.Type.Qualified val Int: jvm.Type.Qualified val IteratorType: jvm.Type.Qualified + + /** Iterator type compatible with Java APIs (gRPC, etc). Java: java.util.Iterator, Kotlin: kotlin.collections.Iterator, Scala: java.util.Iterator */ + val JavaIteratorType: jvm.Type.Qualified = jvm.Type.Qualified(jvm.QIdent("java.util.Iterator")) + val Long: jvm.Type.Qualified val Short: jvm.Type.Qualified val String: jvm.Type.Qualified @@ -83,4 +87,7 @@ trait MutableListSupport { /** Add an element to the mutable list */ def add(list: jvm.Code, element: jvm.Code): jvm.Code + + /** Convert mutable list to immutable list type. Scala: `.toList`, Java/Kotlin: noop */ + def toImmutable(list: jvm.Code): jvm.Code } diff --git a/typr/src/scala/typr/TypeSupportJava.scala b/typr/src/scala/typr/TypeSupportJava.scala index 1075e95b62..adbaa7e4b0 100644 --- a/typr/src/scala/typr/TypeSupportJava.scala +++ b/typr/src/scala/typr/TypeSupportJava.scala @@ -82,6 +82,9 @@ object TypeSupportJava extends TypeSupport { def forEach(collection: jvm.Code, lambda: jvm.Code): jvm.Code = code"$collection.forEach($lambda)" + def forEachStmt(collection: jvm.Code, elem: jvm.Ident, elemType: jvm.Type)(body: jvm.Code => List[jvm.Code]): jvm.Code = + jvm.ForEach(elem, elemType, collection, body(elem.code)).code + def arrayMapToList(array: jvm.Code, mapper: jvm.Code): jvm.Code = code"${TypesJava.Arrays}.stream($array).map($mapper).toList()" @@ -149,6 +152,15 @@ object TypeSupportJava extends TypeSupport { def getNullable(map: jvm.Code, key: jvm.Code): jvm.Code = code"$map.get($key)" + + def forEachEntry(map: jvm.Code, keyType: jvm.Type, valueType: jvm.Type)(body: (jvm.Code, jvm.Code) => List[jvm.Code]): jvm.Code = { + val MapEntryType = jvm.Type.Qualified(jvm.QIdent("java.util.Map.Entry")) + val entry = jvm.Ident("entry") + val keyRef = entry.code.invoke("getKey") + val valRef = entry.code.invoke("getValue") + val entryType = MapEntryType.of(keyType, valueType) + jvm.ForEach(entry, entryType, code"$map.entrySet()", body(keyRef, valRef)).code + } } override object IteratorOps extends IteratorSupport { @@ -178,5 +190,8 @@ object TypeSupportJava extends TypeSupport { def add(list: jvm.Code, element: jvm.Code): jvm.Code = jvm.IgnoreResult(code"$list.add($element)") + + def toImmutable(list: jvm.Code): jvm.Code = + list } } diff --git a/typr/src/scala/typr/TypeSupportScala.scala b/typr/src/scala/typr/TypeSupportScala.scala index 4efcced64d..463760bf12 100644 --- a/typr/src/scala/typr/TypeSupportScala.scala +++ b/typr/src/scala/typr/TypeSupportScala.scala @@ -85,6 +85,13 @@ object TypeSupportScala extends TypeSupport { def forEach(collection: jvm.Code, lambda: jvm.Code): jvm.Code = code"$collection.foreach($lambda)" + def forEachStmt(collection: jvm.Code, elem: jvm.Ident, elemType: jvm.Type)(body: jvm.Code => List[jvm.Code]): jvm.Code = { + val bodyCode = body(elem.code).map(s => code"$s;").mkCode("\n") + code"""|for ($elem <- $collection) { + | $bodyCode + |}""".stripMargin + } + def arrayMapToList(array: jvm.Code, mapper: jvm.Code): jvm.Code = code"$array.map($mapper).toList" @@ -162,6 +169,15 @@ object TypeSupportScala extends TypeSupport { def getNullable(map: jvm.Code, key: jvm.Code): jvm.Code = code"$map.get($key).orNull" + + def forEachEntry(map: jvm.Code, keyType: jvm.Type, valueType: jvm.Type)(body: (jvm.Code, jvm.Code) => List[jvm.Code]): jvm.Code = { + val key = jvm.Ident("k") + val value = jvm.Ident("v") + val bodyCode = body(key.code, value.code).map(s => code"$s;").mkCode("\n") + code"""|for (($key, $value) <- $map) { + | $bodyCode + |}""".stripMargin + } } override object IteratorOps extends IteratorSupport { @@ -183,5 +199,8 @@ object TypeSupportScala extends TypeSupport { def add(list: jvm.Code, element: jvm.Code): jvm.Code = jvm.IgnoreResult(code"$list.addOne($element)") + + def toImmutable(list: jvm.Code): jvm.Code = + code"$list.toList" } } diff --git a/typr/src/scala/typr/avro/codegen/RecordCodegen.scala b/typr/src/scala/typr/avro/codegen/RecordCodegen.scala index 39fd7c1217..930d1f41c7 100644 --- a/typr/src/scala/typr/avro/codegen/RecordCodegen.scala +++ b/typr/src/scala/typr/avro/codegen/RecordCodegen.scala @@ -223,6 +223,7 @@ class RecordCodegen( comments = comments, tpe = tpe, values = values, + members = Nil, staticMembers = Nil ) diff --git a/typr/src/scala/typr/effects/EffectType.scala b/typr/src/scala/typr/effects/EffectType.scala index 2e1394aa46..8297d27660 100644 --- a/typr/src/scala/typr/effects/EffectType.scala +++ b/typr/src/scala/typr/effects/EffectType.scala @@ -41,6 +41,10 @@ object EffectType { val emitterLambda = jvm.Lambda(List(jvm.LambdaParam(em)), jvm.Body.Stmts(List(body))) code"$tpe.createFrom().emitter($emitterLambda)" } + def subscribeWith(effect: jvm.Code, onItem: jvm.Code, onFailure: jvm.Code): jvm.Code = + code"$effect.subscribe().with($onItem, $onFailure)" + def defer(supplier: jvm.Code): jvm.Code = + code"$tpe.createFrom().item($supplier)" } /** Project Reactor Mono operations - used by Spring WebFlux */ @@ -62,6 +66,10 @@ object EffectType { val sinkLambda = jvm.Lambda(List(jvm.LambdaParam(sink)), jvm.Body.Stmts(List(body))) code"$tpe.create($sinkLambda)" } + def subscribeWith(effect: jvm.Code, onItem: jvm.Code, onFailure: jvm.Code): jvm.Code = + code"$effect.subscribe($onItem, $onFailure)" + def defer(supplier: jvm.Code): jvm.Code = + code"$tpe.fromSupplier($supplier)" } /** Java CompletableFuture operations */ @@ -94,6 +102,19 @@ object EffectType { val identityLambda = jvm.Lambda(List(jvm.LambdaParam(f)), jvm.Body.Expr(f.code)) code"$tpe.supplyAsync($supplierLambda).thenCompose($identityLambda)" } + def subscribeWith(effect: jvm.Code, onItem: jvm.Code, onFailure: jvm.Code): jvm.Code = { + val t = jvm.Ident("t") + val exceptionallyBody = jvm.Body.Stmts( + List( + onFailure.invoke("accept", t.code), + jvm.Return(code"null").code + ) + ) + val exceptionallyLambda = jvm.Lambda(List(jvm.LambdaParam(t)), exceptionallyBody) + code"$effect.thenAccept($onItem).exceptionally($exceptionallyLambda)" + } + def defer(supplier: jvm.Code): jvm.Code = + code"$tpe.supplyAsync($supplier)" } /** Cats Effect IO operations */ @@ -117,6 +138,12 @@ object EffectType { val cbLambda = jvm.Lambda(List(jvm.LambdaParam(cb)), jvm.Body.Stmts(List(body))) code"$tpe.async_($cbLambda)" } + def subscribeWith(effect: jvm.Code, onItem: jvm.Code, onFailure: jvm.Code): jvm.Code = { + val result = jvm.Ident("result") + code"{ val $result = $effect.unsafeRunSync()(cats.effect.unsafe.IORuntime.global); $onItem.apply($result) }" + } + def defer(supplier: jvm.Code): jvm.Code = + code"$tpe.delay($supplier.apply())" } /** ZIO Task operations */ @@ -139,6 +166,15 @@ object EffectType { val cbLambda = jvm.Lambda(List(jvm.LambdaParam(cb)), jvm.Body.Stmts(List(body))) code"zio.ZIO.async($cbLambda)" } + def subscribeWith(effect: jvm.Code, onItem: jvm.Code, onFailure: jvm.Code): jvm.Code = { + val UnsafeType = jvm.Type.Qualified(jvm.QIdent("zio.Unsafe")) + val RuntimeType = jvm.Type.Qualified(jvm.QIdent("zio.Runtime")) + val u = jvm.Ident("u") + val result = jvm.Ident("result") + code"$UnsafeType.unsafe { implicit $u => val $result = $RuntimeType.default.unsafe.run($effect).getOrThrowFiberFailure(); $onItem.apply($result) }" + } + def defer(supplier: jvm.Code): jvm.Code = + code"zio.ZIO.attempt($supplier.apply())" } /** SmallRye Mutiny Uni - used by Quarkus */ diff --git a/typr/src/scala/typr/effects/EffectTypeOps.scala b/typr/src/scala/typr/effects/EffectTypeOps.scala index 2005130917..7ccf0f540a 100644 --- a/typr/src/scala/typr/effects/EffectTypeOps.scala +++ b/typr/src/scala/typr/effects/EffectTypeOps.scala @@ -56,4 +56,10 @@ trait EffectTypeOps { * The complete async expression wrapped in the effect type */ def async(resultType: jvm.Type)(bodyBuilder: (jvm.Code => jvm.Code, jvm.Code => jvm.Code) => jvm.Code): jvm.Code + + /** Subscribe to an effect with success/failure callbacks (fire-and-forget). Used by gRPC server adapter. */ + def subscribeWith(effect: jvm.Code, onItem: jvm.Code, onFailure: jvm.Code): jvm.Code + + /** Create an effect that lazily evaluates a supplier. Used by gRPC client wrapper. */ + def defer(supplier: jvm.Code): jvm.Code } diff --git a/typr/src/scala/typr/grpc/ComputedProtobufWrapper.scala b/typr/src/scala/typr/grpc/ComputedProtobufWrapper.scala new file mode 100644 index 0000000000..da8511b8a4 --- /dev/null +++ b/typr/src/scala/typr/grpc/ComputedProtobufWrapper.scala @@ -0,0 +1,64 @@ +package typr.grpc + +import typr.jvm + +/** Computed wrapper type extracted from (typr.wrapper) annotations on Protobuf fields. + * + * This follows the Computed* pattern used by database codegen (ComputedDomain, etc.) and Avro codegen (ComputedAvroWrapper). + */ +case class ComputedProtobufWrapper( + /** Qualified wrapper type name (e.g., com.example.CustomerId) */ + tpe: jvm.Type.Qualified, + /** Underlying JVM type (e.g., Long, String, java.util.UUID) */ + underlyingJvmType: jvm.Type, + /** Underlying Proto type (for converter logic) */ + underlyingProtoType: ProtoType +) + +object ComputedProtobufWrapper { + + /** Collect all unique wrapper types from messages. + * + * Scans all fields with (typr.wrapper) custom option and builds ComputedProtobufWrapper instances. + * + * @param messages + * All ProtoMessages to scan (should include nested messages flattened) + * @param typeMapper + * Maps Proto types to JVM types + * @param naming + * Naming conventions for generating qualified type names + * @return + * List of unique wrapper types to generate + */ + def collect( + messages: List[ProtoMessage], + typeMapper: typr.grpc.codegen.ProtobufTypeMapper, + naming: typr.Naming + ): List[ComputedProtobufWrapper] = { + messages + .flatMap { message => + message.fields.flatMap { field => + field.wrapperType.map { wrapperName => + val underlyingProtoType = unwrapOptional(field) + val underlyingJvmType = typeMapper.mapType(underlyingProtoType) + val tpe = naming.grpcWrapperTypeName(wrapperName) + ComputedProtobufWrapper( + tpe = tpe, + underlyingJvmType = underlyingJvmType, + underlyingProtoType = underlyingProtoType + ) + } + } + } + .distinctBy(_.tpe.value.idents.map(_.value).mkString(".")) + } + + /** Get the base type of a field, unwrapping optional if present */ + private def unwrapOptional(field: ProtoField): ProtoType = { + if (field.proto3Optional) { + field.fieldType + } else { + ProtoType.unwrapWellKnown(field.fieldType).getOrElse(field.fieldType) + } + } +} diff --git a/typr/src/scala/typr/grpc/GrpcCodegen.scala b/typr/src/scala/typr/grpc/GrpcCodegen.scala new file mode 100644 index 0000000000..e51cfd4cc2 --- /dev/null +++ b/typr/src/scala/typr/grpc/GrpcCodegen.scala @@ -0,0 +1,201 @@ +package typr.grpc + +import typr.grpc.codegen._ +import typr.grpc.parser.{GrpcParseError, ProtobufParser} +import typr.{jvm, Lang, Naming, Scope} +import typr.avro.codegen.FileAvroWrapper +import typr.internal.codegen._ +import typr.jvm.Code.{CodeOps, TreeOps} +import typr.openapi.codegen.NoJsonLibSupport + +/** Main entry point for gRPC/Protobuf code generation */ +object GrpcCodegen { + + case class Result( + files: List[jvm.File], + errors: List[String] + ) + + /** Generate code from Protobuf .proto files */ + def generate( + options: GrpcOptions, + lang: Lang + ): Result = { + ProtobufParser.parse(options.protoSource) match { + case Left(error) => + Result(Nil, List(error.message)) + case Right(protoFiles) => + generateFromProtoFiles(protoFiles, options, lang) + } + } + + /** Generate code from parsed proto files */ + private def generateFromProtoFiles( + protoFiles: List[ProtoFile], + options: GrpcOptions, + lang: Lang + ): Result = { + val naming = new Naming(options.pkg, lang) + + // Resolve map fields in all proto files + val resolvedFiles = protoFiles.map(ProtobufParser.resolveMapFields) + + // Flatten all messages (including nested) across all files + val allMessages = resolvedFiles.flatMap(f => flattenMessages(f.messages)) + val allEnums = resolvedFiles.flatMap(f => flattenEnums(f.messages, f.enums)) + val allServices = resolvedFiles.flatMap(_.services) + + // Create base type mapper (without wrapper types) for wrapper collection + val baseTypeMapper = new ProtobufTypeMapper( + lang = lang, + naming = naming, + wrapperTypeMap = Map.empty + ) + + // Collect wrapper types from all messages + val computedWrappers = ComputedProtobufWrapper.collect(allMessages, baseTypeMapper, naming) + + // Build wrapper type lookup map: wrapperName -> QualifiedType + val wrapperTypeMap: Map[String, jvm.Type.Qualified] = + computedWrappers.map { w => + w.tpe.value.name.value -> w.tpe + }.toMap + + // Create type mapper with wrapper types + val typeMapper = new ProtobufTypeMapper( + lang = lang, + naming = naming, + wrapperTypeMap = wrapperTypeMap + ) + + val files = List.newBuilder[jvm.File] + val errors = List.newBuilder[String] + + // Generate wrapper types + if (options.generateMessages && computedWrappers.nonEmpty) { + computedWrappers.foreach { wrapper => + files += generateWrapperType(wrapper, lang) + } + } + + // Generate message classes + if (options.generateMessages) { + val messageCodegen = new MessageCodegen( + naming = naming, + typeMapper = typeMapper, + lang = lang, + wrapperTypeMap = wrapperTypeMap + ) + + allMessages.foreach { message => + try { + files += messageCodegen.generate(message) + + // Generate oneof types for this message + val oneofCodegen = new OneOfCodegen(naming, typeMapper, lang) + message.oneofs.foreach { oneof => + files += oneofCodegen.generate(message.fullName, oneof) + } + } catch { + case e: Exception => + errors += s"Failed to generate message ${message.name}: ${e.getMessage}" + } + } + + // Generate enum classes + val enumCodegen = new EnumCodegen(naming, lang) + allEnums.foreach { protoEnum => + try { + files += enumCodegen.generate(protoEnum) + } catch { + case e: Exception => + errors += s"Failed to generate enum ${protoEnum.name}: ${e.getMessage}" + } + } + } + + // Generate service interfaces, servers, and clients + if (allServices.nonEmpty && (options.generateServices || options.generateServers || options.generateClients)) { + val serviceCodegen = new ServiceCodegen(naming, lang, options, typeMapper) + + allServices.foreach { service => + try { + files ++= serviceCodegen.generate(service) + } catch { + case e: Exception => + errors += s"Failed to generate service ${service.name}: ${e.getMessage}" + } + } + } + + Result(files.result(), errors.result()) + } + + /** Generate a wrapper type file */ + private def generateWrapperType(wrapper: ComputedProtobufWrapper, lang: Lang): jvm.File = { + val value = jvm.Ident("value") + val v = jvm.Ident("v") + val thisRef = code"this" + + val valueOfMethod = jvm.Method( + annotations = Nil, + comments = jvm.Comments(List(s"Create a ${wrapper.tpe.value.name.value} from a raw value")), + tparams = Nil, + name = jvm.Ident("valueOf"), + params = List(jvm.Param(Nil, jvm.Comments.Empty, v, wrapper.underlyingJvmType, None)), + implicitParams = Nil, + tpe = wrapper.tpe, + throws = Nil, + body = jvm.Body.Stmts(List(jvm.Return(jvm.New(wrapper.tpe.code, List(jvm.Arg.Pos(v.code))).code).code)), + isOverride = false, + isDefault = false + ) + + val unwrapMethod = jvm.Method( + annotations = Nil, + comments = jvm.Comments(List("Get the underlying value")), + tparams = Nil, + name = jvm.Ident("unwrap"), + params = Nil, + implicitParams = Nil, + tpe = wrapper.underlyingJvmType, + throws = Nil, + body = jvm.Body.Stmts(List(jvm.Return(lang.prop(thisRef, value)).code)), + isOverride = false, + isDefault = false + ) + + val record = jvm.Adt.Record( + annotations = Nil, + constructorAnnotations = Nil, + isWrapper = true, + privateConstructor = false, + comments = jvm.Comments(List(s"Wrapper type for ${wrapper.underlyingJvmType.render}")), + name = wrapper.tpe, + tparams = Nil, + params = List(jvm.Param(Nil, jvm.Comments.Empty, value, wrapper.underlyingJvmType, None)), + implicitParams = Nil, + `extends` = None, + implements = Nil, + members = List(unwrapMethod), + staticMembers = List(valueOfMethod) + ) + + jvm.File(wrapper.tpe, jvm.Code.Tree(record), secondaryTypes = Nil, scope = Scope.Main) + } + + /** Flatten all messages including nested ones */ + private def flattenMessages(messages: List[ProtoMessage]): List[ProtoMessage] = { + messages.flatMap { msg => + msg :: flattenMessages(msg.nestedMessages) + } + } + + /** Collect all enums including nested ones from messages */ + private def flattenEnums(messages: List[ProtoMessage], topLevelEnums: List[ProtoEnum]): List[ProtoEnum] = { + val nestedEnums = messages.flatMap { msg => + msg.nestedEnums ++ flattenEnums(msg.nestedMessages, Nil) + } + topLevelEnums ++ nestedEnums + } +} diff --git a/typr/src/scala/typr/grpc/GrpcOptions.scala b/typr/src/scala/typr/grpc/GrpcOptions.scala new file mode 100644 index 0000000000..8b87c473c3 --- /dev/null +++ b/typr/src/scala/typr/grpc/GrpcOptions.scala @@ -0,0 +1,104 @@ +package typr.grpc + +import typr.jvm +import typr.effects.EffectType + +import java.nio.file.Path + +/** Configuration options for gRPC/Protobuf code generation */ +case class GrpcOptions( + /** Base package for generated code */ + pkg: jvm.QIdent, + /** Proto source location */ + protoSource: ProtoSource, + /** Whether to generate message classes (clean JVM types) */ + generateMessages: Boolean, + /** Whether to generate service interfaces */ + generateServices: Boolean, + /** Whether to generate server adapters (extends grpc-java *ImplBase) */ + generateServers: Boolean, + /** Whether to generate client wrappers (wraps grpc-java stubs) */ + generateClients: Boolean, + /** Effect type for async/reactive operations */ + effectType: EffectType, + /** Framework integration for gRPC (Spring, Quarkus, or None for framework-agnostic code) */ + frameworkIntegration: GrpcFrameworkIntegration +) + +object GrpcOptions { + def default(pkg: jvm.QIdent, protoSource: ProtoSource): GrpcOptions = + GrpcOptions( + pkg = pkg, + protoSource = protoSource, + generateMessages = true, + generateServices = true, + generateServers = true, + generateClients = true, + effectType = EffectType.Blocking, + frameworkIntegration = GrpcFrameworkIntegration.None + ) +} + +/** Proto source for gRPC code generation */ +sealed trait ProtoSource + +object ProtoSource { + + /** Load protos from a directory containing .proto files. + * + * @param path + * Directory containing .proto files + * @param includePaths + * Additional include paths for proto imports (e.g., for well-known types). The source directory is always included. + */ + case class Directory(path: Path, includePaths: List[Path]) extends ProtoSource + + object Directory { + def apply(path: Path): Directory = Directory(path, Nil) + } + + /** Use a pre-built FileDescriptorSet file. + * + * For users who already run protoc in their build and have a descriptor set file. + */ + case class DescriptorSet(path: Path) extends ProtoSource +} + +/** Framework integration for gRPC server/client generation. + * + * This determines which annotations and types are used in generated code: + * - None: Generate framework-agnostic code (default) + * - Spring: Use spring-grpc annotations (@GrpcService, @ImportGrpcClients) + * - Quarkus: Use quarkus-grpc annotations (@GrpcService, @GrpcClient) + */ +sealed trait GrpcFrameworkIntegration { + import typr.grpc.codegen.GrpcFramework + + /** Get the GrpcFramework implementation for this integration, if any */ + def grpcFramework: Option[GrpcFramework] +} + +object GrpcFrameworkIntegration { + import typr.grpc.codegen.{GrpcFrameworkSpring, GrpcFrameworkQuarkus} + + /** No framework annotations - generate framework-agnostic code */ + case object None extends GrpcFrameworkIntegration { + override def grpcFramework: Option[typr.grpc.codegen.GrpcFramework] = scala.None + } + + /** Spring gRPC integration. + * + * Server: @GrpcService annotation, auto-registered as BindableService Client: @ImportGrpcClients + @Autowired stub injection + */ + case object Spring extends GrpcFrameworkIntegration { + override def grpcFramework: Option[typr.grpc.codegen.GrpcFramework] = Some(GrpcFrameworkSpring) + } + + /** Quarkus gRPC integration. + * + * Server: @GrpcService + @Singleton annotation Client: @GrpcClient("service-name") field injection + */ + case object Quarkus extends GrpcFrameworkIntegration { + override def grpcFramework: Option[typr.grpc.codegen.GrpcFramework] = Some(GrpcFrameworkQuarkus) + } +} diff --git a/typr/src/scala/typr/grpc/GrpcTypes.scala b/typr/src/scala/typr/grpc/GrpcTypes.scala new file mode 100644 index 0000000000..33f79fda01 --- /dev/null +++ b/typr/src/scala/typr/grpc/GrpcTypes.scala @@ -0,0 +1,281 @@ +package typr.grpc + +/** Internal representation of parsed Protobuf schemas. + * + * These types represent the Protobuf schema structure after parsing from .proto files via protoc descriptor sets. They abstract over the protobuf-java library's descriptor classes to provide a + * cleaner API for code generation. + */ + +/** A complete .proto file, potentially containing multiple types and services */ +case class ProtoFile( + /** Package name from the .proto file */ + protoPackage: Option[String], + /** Java package from option java_package, if set */ + javaPackage: Option[String], + /** All top-level message types defined in this file */ + messages: List[ProtoMessage], + /** All top-level enum types defined in this file */ + enums: List[ProtoEnum], + /** All services defined in this file */ + services: List[ProtoService], + /** Source file path (if loaded from file) */ + sourcePath: Option[String], + /** Syntax version (proto2 or proto3) */ + syntax: ProtoSyntax +) + +/** Protobuf syntax version */ +sealed trait ProtoSyntax + +object ProtoSyntax { + case object Proto2 extends ProtoSyntax + case object Proto3 extends ProtoSyntax +} + +/** A protobuf message type */ +case class ProtoMessage( + /** Simple name of the message (e.g., "CustomerOrder") */ + name: String, + /** Full qualified name including package (e.g., "com.example.CustomerOrder") */ + fullName: String, + /** Fields in this message */ + fields: List[ProtoField], + /** Nested message types defined inside this message */ + nestedMessages: List[ProtoMessage], + /** Nested enum types defined inside this message */ + nestedEnums: List[ProtoEnum], + /** Oneof groups defined in this message */ + oneofs: List[ProtoOneof], + /** Whether this is a map entry type (auto-generated for map fields) */ + isMapEntry: Boolean +) + +/** A field within a protobuf message */ +case class ProtoField( + /** Field name as defined in .proto */ + name: String, + /** Field number */ + number: Int, + /** Field type */ + fieldType: ProtoType, + /** Field label (optional, repeated, required, or implicit for proto3) */ + label: ProtoFieldLabel, + /** Wrapper type name from (typr.wrapper) custom option */ + wrapperType: Option[String], + /** Default value (proto2 only, JSON-encoded) */ + defaultValue: Option[String], + /** Whether this field is part of a oneof */ + oneofIndex: Option[Int], + /** Whether this field uses proto3 optional keyword */ + proto3Optional: Boolean +) { + + /** Whether this field is optional (proto3 optional, or nullable wrapper type) */ + def isOptional: Boolean = proto3Optional + + /** Whether this field is repeated (list or map) */ + def isRepeated: Boolean = label == ProtoFieldLabel.Repeated && !isMapField + + /** Whether this field represents a map */ + def isMapField: Boolean = fieldType match { + case ProtoType.Map(_, _) => true + case _ => false + } +} + +/** Field label in protobuf */ +sealed trait ProtoFieldLabel + +object ProtoFieldLabel { + case object Optional extends ProtoFieldLabel + case object Required extends ProtoFieldLabel + case object Repeated extends ProtoFieldLabel +} + +/** A oneof group within a message */ +case class ProtoOneof( + /** Name of the oneof group */ + name: String, + /** Fields that are part of this oneof */ + fields: List[ProtoField] +) + +/** A protobuf enum type */ +case class ProtoEnum( + /** Simple name of the enum */ + name: String, + /** Full qualified name including package */ + fullName: String, + /** Enum values */ + values: List[ProtoEnumValue], + /** Whether the enum allows aliases (allow_alias option) */ + allowAlias: Boolean +) + +/** A single enum value */ +case class ProtoEnumValue( + /** Name of the enum value (e.g., "ORDER_STATUS_PENDING") */ + name: String, + /** Numeric value */ + number: Int +) + +/** A protobuf service definition */ +case class ProtoService( + /** Simple name of the service */ + name: String, + /** Full qualified name including package */ + fullName: String, + /** RPC methods defined in this service */ + methods: List[ProtoMethod] +) + +/** An RPC method in a protobuf service */ +case class ProtoMethod( + /** Method name (e.g., "GetCustomer") */ + name: String, + /** Input message type (full name) */ + inputType: String, + /** Output message type (full name) */ + outputType: String, + /** Whether client sends a stream */ + clientStreaming: Boolean, + /** Whether server sends a stream */ + serverStreaming: Boolean +) { + + /** RPC pattern based on streaming flags */ + def rpcPattern: RpcPattern = (clientStreaming, serverStreaming) match { + case (false, false) => RpcPattern.Unary + case (false, true) => RpcPattern.ServerStreaming + case (true, false) => RpcPattern.ClientStreaming + case (true, true) => RpcPattern.BidiStreaming + } +} + +/** The four gRPC RPC patterns */ +sealed trait RpcPattern + +object RpcPattern { + case object Unary extends RpcPattern + case object ServerStreaming extends RpcPattern + case object ClientStreaming extends RpcPattern + case object BidiStreaming extends RpcPattern +} + +/** Protobuf type (field types, etc.) */ +sealed trait ProtoType + +object ProtoType { + + // Scalar types + case object Double extends ProtoType + case object Float extends ProtoType + case object Int32 extends ProtoType + case object Int64 extends ProtoType + case object UInt32 extends ProtoType + case object UInt64 extends ProtoType + case object SInt32 extends ProtoType + case object SInt64 extends ProtoType + case object Fixed32 extends ProtoType + case object Fixed64 extends ProtoType + case object SFixed32 extends ProtoType + case object SFixed64 extends ProtoType + case object Bool extends ProtoType + case object String extends ProtoType + case object Bytes extends ProtoType + + // Complex types + + /** Reference to a named message type */ + case class Message(fullName: String) extends ProtoType + + /** Reference to a named enum type */ + case class Enum(fullName: String) extends ProtoType + + /** Map type (auto-generated from map syntax) */ + case class Map(keyType: ProtoType, valueType: ProtoType) extends ProtoType + + // Well-known types (google.protobuf.*) + + /** google.protobuf.Timestamp -> java.time.Instant */ + case object Timestamp extends ProtoType + + /** google.protobuf.Duration -> java.time.Duration */ + case object Duration extends ProtoType + + /** google.protobuf.StringValue -> optional String */ + case object StringValue extends ProtoType + + /** google.protobuf.Int32Value -> optional Int */ + case object Int32Value extends ProtoType + + /** google.protobuf.Int64Value -> optional Long */ + case object Int64Value extends ProtoType + + /** google.protobuf.UInt32Value -> optional Int */ + case object UInt32Value extends ProtoType + + /** google.protobuf.UInt64Value -> optional Long */ + case object UInt64Value extends ProtoType + + /** google.protobuf.FloatValue -> optional Float */ + case object FloatValue extends ProtoType + + /** google.protobuf.DoubleValue -> optional Double */ + case object DoubleValue extends ProtoType + + /** google.protobuf.BoolValue -> optional Boolean */ + case object BoolValue extends ProtoType + + /** google.protobuf.BytesValue -> optional ByteString */ + case object BytesValue extends ProtoType + + /** google.protobuf.Any -> pass-through */ + case object Any extends ProtoType + + /** google.protobuf.Struct -> Map */ + case object Struct extends ProtoType + + /** google.protobuf.Empty -> Void */ + case object Empty extends ProtoType + + /** Check if a type is a well-known wrapper type */ + def isWrapperType(tpe: ProtoType): Boolean = tpe match { + case StringValue | Int32Value | Int64Value | UInt32Value | UInt64Value | FloatValue | DoubleValue | BoolValue | BytesValue => + true + case _ => false + } + + /** Get the underlying scalar type for a well-known wrapper type */ + def unwrapWellKnown(tpe: ProtoType): Option[ProtoType] = tpe match { + case StringValue => Some(String) + case Int32Value => Some(Int32) + case Int64Value => Some(Int64) + case UInt32Value => Some(UInt32) + case UInt64Value => Some(UInt64) + case FloatValue => Some(Float) + case DoubleValue => Some(Double) + case BoolValue => Some(Bool) + case BytesValue => Some(Bytes) + case _ => None + } + + /** Well-known type full names for detection during parsing */ + val wellKnownTypes: scala.collection.immutable.Map[scala.Predef.String, ProtoType] = scala.collection.immutable.Map( + "google.protobuf.Timestamp" -> Timestamp, + "google.protobuf.Duration" -> Duration, + "google.protobuf.StringValue" -> StringValue, + "google.protobuf.Int32Value" -> Int32Value, + "google.protobuf.Int64Value" -> Int64Value, + "google.protobuf.UInt32Value" -> UInt32Value, + "google.protobuf.UInt64Value" -> UInt64Value, + "google.protobuf.FloatValue" -> FloatValue, + "google.protobuf.DoubleValue" -> DoubleValue, + "google.protobuf.BoolValue" -> BoolValue, + "google.protobuf.BytesValue" -> BytesValue, + "google.protobuf.Any" -> Any, + "google.protobuf.Struct" -> Struct, + "google.protobuf.Empty" -> Empty + ) +} diff --git a/typr/src/scala/typr/grpc/codegen/EnumCodegen.scala b/typr/src/scala/typr/grpc/codegen/EnumCodegen.scala new file mode 100644 index 0000000000..f0659a8ac8 --- /dev/null +++ b/typr/src/scala/typr/grpc/codegen/EnumCodegen.scala @@ -0,0 +1,111 @@ +package typr.grpc.codegen + +import typr.grpc._ +import typr.{jvm, Lang, Naming, Scope, NonEmptyList} +import typr.jvm.Code.{CodeOps, TreeOps, TypeOps} +import typr.internal.codegen._ + +/** Generates jvm.File for Protobuf enum types. + * + * Proto enums are mapped to JVM enums with toValue()/fromValue() for wire format encoding. The toValue() method returns the protobuf numeric value and fromValue(int) does the reverse lookup. + */ +class EnumCodegen( + naming: Naming, + lang: Lang +) { + + /** Generate an enum file from a ProtoEnum */ + def generate(protoEnum: ProtoEnum): jvm.File = { + val tpe = naming.grpcEnumTypeName(protoEnum.fullName) + + // Filter out aliases (same numeric value) - keep first occurrence + val uniqueValues = protoEnum.values.foldLeft(List.empty[ProtoEnumValue]) { (acc, v) => + if (acc.exists(_.number == v.number)) acc + else acc :+ v + } + + val values = NonEmptyList + .fromList(uniqueValues.map { value => + (naming.grpcEnumValueName(value.name), jvm.StrLit(value.name).code) + }) + .getOrElse(sys.error(s"Enum ${protoEnum.name} has no values")) + + // toValue() instance method - returns the protobuf numeric value + // Use if-else with return in each branch (valid in both Java and Scala) + val toValueCases: List[(jvm.Code, jvm.Code)] = uniqueValues.map { value => + val cond = code"this".callNullary("toString").invoke("equals", jvm.StrLit(value.name).code) + val body = jvm.Return(jvm.Code.Str(value.number.toString)).code + (cond, body) + } + val toValueDefault = jvm.Return(jvm.Code.Str("0")).code + + val toValueBody = if (toValueCases.nonEmpty) { + jvm.IfElseChain(toValueCases, toValueDefault).code + } else { + toValueDefault + } + + val toValueMethod = jvm.Method( + annotations = Nil, + comments = jvm.Comments.Empty, + tparams = Nil, + name = jvm.Ident("toValue"), + params = Nil, + implicitParams = Nil, + tpe = lang.Int, + throws = Nil, + body = jvm.Body.Stmts(List(toValueBody)), + isOverride = false, + isDefault = false + ) + + // fromValue(int) static method - reverse lookup from protobuf number to enum constant + val fromValueParam = jvm.Param(Nil, jvm.Comments.Empty, jvm.Ident("value"), lang.Int, None) + + val fromValueCases: List[(jvm.Code, jvm.Code)] = uniqueValues.map { value => + val cond = code"${fromValueParam.name.code} == ${jvm.Code.Str(value.number.toString)}" + val body = jvm.Return(tpe.code.select(naming.grpcEnumValueName(value.name).value)).code + (cond, body) + } + + val throwExpr = jvm + .Throw( + jvm.Type + .Qualified("java.lang.IllegalArgumentException") + .construct(code""""Unknown enum value: " + ${fromValueParam.name.code}""") + ) + .code + + val fromValueBody = if (fromValueCases.nonEmpty) { + jvm.IfElseChain(fromValueCases, throwExpr).code + } else { + throwExpr + } + + val fromValueMethod = jvm.Method( + annotations = Nil, + comments = jvm.Comments.Empty, + tparams = Nil, + name = jvm.Ident("fromValue"), + params = List(fromValueParam), + implicitParams = Nil, + tpe = tpe, + throws = Nil, + body = jvm.Body.Stmts(List(fromValueBody)), + isOverride = false, + isDefault = false + ) + + val enumTree = jvm.Enum( + annotations = Nil, + comments = jvm.Comments.Empty, + tpe = tpe, + values = values, + members = List(toValueMethod), + staticMembers = List(fromValueMethod) + ) + + val generatedCode = jvm.Code.Tree(enumTree) + jvm.File(tpe, generatedCode, secondaryTypes = Nil, scope = Scope.Main) + } +} diff --git a/typr/src/scala/typr/grpc/codegen/GrpcFramework.scala b/typr/src/scala/typr/grpc/codegen/GrpcFramework.scala new file mode 100644 index 0000000000..97d178c1ec --- /dev/null +++ b/typr/src/scala/typr/grpc/codegen/GrpcFramework.scala @@ -0,0 +1,19 @@ +package typr.grpc.codegen + +import typr.jvm + +/** Framework integration for generating gRPC server/client with framework-specific annotations. + * + * Implementations provide framework-specific types and annotations for Spring gRPC and Quarkus gRPC. + */ +trait GrpcFramework { + + /** Server service class annotation (e.g., @GrpcService, @Singleton) */ + def serverAnnotations: List[jvm.Annotation] + + /** Client injection annotation */ + def clientFieldAnnotations(serviceName: String): List[jvm.Annotation] + + /** Constructor injection annotations */ + def constructorAnnotations: List[jvm.Annotation] +} diff --git a/typr/src/scala/typr/grpc/codegen/GrpcFrameworkQuarkus.scala b/typr/src/scala/typr/grpc/codegen/GrpcFrameworkQuarkus.scala new file mode 100644 index 0000000000..7789e62645 --- /dev/null +++ b/typr/src/scala/typr/grpc/codegen/GrpcFrameworkQuarkus.scala @@ -0,0 +1,29 @@ +package typr.grpc.codegen + +import typr.jvm +import typr.internal.codegen._ + +/** Quarkus gRPC framework integration. + * + * Server: @GrpcService + @Singleton annotations Client: @GrpcClient("service-name") field injection + */ +object GrpcFrameworkQuarkus extends GrpcFramework { + + // Quarkus/CDI annotations + private val GrpcServiceAnnotation: jvm.Type.Qualified = jvm.Type.Qualified(jvm.QIdent("io.quarkus.grpc.GrpcService")) + private val Singleton: jvm.Type.Qualified = jvm.Type.Qualified(jvm.QIdent("jakarta.inject.Singleton")) + private val GrpcClient: jvm.Type.Qualified = jvm.Type.Qualified(jvm.QIdent("io.quarkus.grpc.GrpcClient")) + private val Inject: jvm.Type.Qualified = jvm.Type.Qualified(jvm.QIdent("jakarta.inject.Inject")) + + override def serverAnnotations: List[jvm.Annotation] = + List( + jvm.Annotation(GrpcServiceAnnotation, Nil), + jvm.Annotation(Singleton, Nil) + ) + + override def clientFieldAnnotations(serviceName: String): List[jvm.Annotation] = + List(jvm.Annotation(GrpcClient, List(jvm.Annotation.Arg.Positional(jvm.StrLit(serviceName).code)))) + + override def constructorAnnotations: List[jvm.Annotation] = + List(jvm.Annotation(Inject, Nil)) +} diff --git a/typr/src/scala/typr/grpc/codegen/GrpcFrameworkSpring.scala b/typr/src/scala/typr/grpc/codegen/GrpcFrameworkSpring.scala new file mode 100644 index 0000000000..cddc1d6113 --- /dev/null +++ b/typr/src/scala/typr/grpc/codegen/GrpcFrameworkSpring.scala @@ -0,0 +1,24 @@ +package typr.grpc.codegen + +import typr.jvm + +/** Spring gRPC framework integration. + * + * Server: @GrpcService annotation on BindableService implementations Client: @Autowired for stub injection + */ +object GrpcFrameworkSpring extends GrpcFramework { + + // Spring gRPC annotations + private val GrpcService: jvm.Type.Qualified = jvm.Type.Qualified(jvm.QIdent("org.springframework.grpc.server.service.GrpcService")) + private val Service: jvm.Type.Qualified = jvm.Type.Qualified(jvm.QIdent("org.springframework.stereotype.Service")) + + override def serverAnnotations: List[jvm.Annotation] = + List( + jvm.Annotation(GrpcService, Nil), + jvm.Annotation(Service, Nil) + ) + + override def clientFieldAnnotations(serviceName: String): List[jvm.Annotation] = Nil + + override def constructorAnnotations: List[jvm.Annotation] = Nil +} diff --git a/typr/src/scala/typr/grpc/codegen/MessageCodegen.scala b/typr/src/scala/typr/grpc/codegen/MessageCodegen.scala new file mode 100644 index 0000000000..e7e4700f62 --- /dev/null +++ b/typr/src/scala/typr/grpc/codegen/MessageCodegen.scala @@ -0,0 +1,1079 @@ +package typr.grpc.codegen + +import typr.grpc._ +import typr.{jvm, Lang, Naming, Scope} +import typr.jvm.Code.{CodeOps, TreeOps, TypeOps} +import typr.internal.codegen._ + +/** Generates jvm.File for Protobuf message types. + * + * Proto messages are mapped to clean JVM records/data classes/case classes with direct protobuf binary serialization via CodedOutputStream/CodedInputStream. + */ +class MessageCodegen( + naming: Naming, + typeMapper: ProtobufTypeMapper, + lang: Lang, + wrapperTypeMap: Map[String, jvm.Type.Qualified] +) { + + private val CodedOutputStreamType = jvm.Type.Qualified(jvm.QIdent("com.google.protobuf.CodedOutputStream")) + private val CodedInputStreamType = jvm.Type.Qualified(jvm.QIdent("com.google.protobuf.CodedInputStream")) + private val WireFormatType = jvm.Type.Qualified(jvm.QIdent("com.google.protobuf.WireFormat")) + private val ByteArrayInputStreamType = jvm.Type.Qualified(jvm.QIdent("java.io.ByteArrayInputStream")) + private val InputStreamType = jvm.Type.Qualified(jvm.QIdent("java.io.InputStream")) + private val IOException = jvm.Type.Qualified("java.io.IOException") + private val MethodDescriptorType = jvm.Type.Qualified(jvm.QIdent("io.grpc.MethodDescriptor")) + private val MarshallerType = MethodDescriptorType / jvm.Ident("Marshaller") + + /** Generate a record class from a ProtoMessage */ + def generate(message: ProtoMessage): jvm.File = { + val tpe = naming.grpcMessageTypeName(message.fullName) + + val regularFields = message.fields.filter(_.oneofIndex.isEmpty) + + val params = regularFields.map { field => + val fieldType = typeMapper.mapFieldType(field) + val effectiveType = field.fieldType match { + case ProtoType.Message(_) if !field.isRepeated && !field.isMapField && !field.proto3Optional => + lang.nullableRefType(fieldType) + case _ => fieldType + } + jvm.Param( + annotations = Nil, + comments = jvm.Comments.Empty, + name = naming.grpcFieldName(field.name), + tpe = effectiveType, + default = None + ) + } + + val oneofParams = message.oneofs.map { oneof => + val oneofType = naming.grpcOneofTypeName(message.fullName, oneof.name) + jvm.Param( + annotations = Nil, + comments = jvm.Comments.Empty, + name = naming.grpcFieldName(oneof.name), + tpe = lang.nullableRefType(oneofType), + default = None + ) + } + + val allParams = params ++ oneofParams + + val writeTo = generateWriteToMethod(message) + val getSerializedSize = generateGetSerializedSizeMethod(message) + val parseFrom = generateParseFromMethod(message, tpe) + val marshaller = generateMarshallerField(tpe) + + val recordAdt = jvm.Adt.Record( + annotations = Nil, + constructorAnnotations = Nil, + isWrapper = false, + privateConstructor = false, + comments = jvm.Comments.Empty, + name = tpe, + tparams = Nil, + params = allParams, + implicitParams = Nil, + `extends` = None, + implements = Nil, + members = List(writeTo, getSerializedSize), + staticMembers = List(parseFrom, marshaller) + ) + + jvm.File(tpe, jvm.Code.Tree(recordAdt), secondaryTypes = Nil, scope = Scope.Main) + } + + private def intCode(n: Int): jvm.Code = jvm.Code.Str(n.toString) + + /** Generate writeTo(CodedOutputStream) method */ + private def generateWriteToMethod(message: ProtoMessage): jvm.Method = { + val outputParam = jvm.Param(Nil, jvm.Comments.Empty, jvm.Ident("output"), CodedOutputStreamType, None) + val output = outputParam.name.code + + val regularFields = message.fields.filter(_.oneofIndex.isEmpty) + + val fieldWrites = regularFields.flatMap { field => + generateFieldWrite(output, field) + } + + val oneofWrites = message.oneofs.flatMap { oneof => + generateOneofWrite(output, oneof, message.fullName) + } + + jvm.Method( + annotations = Nil, + comments = jvm.Comments.Empty, + tparams = Nil, + name = jvm.Ident("writeTo"), + params = List(outputParam), + implicitParams = Nil, + tpe = jvm.Type.Void, + throws = List(IOException), + body = jvm.Body.Stmts(fieldWrites ++ oneofWrites), + isOverride = false, + isDefault = false + ) + } + + /** Generate write statements for a single field */ + private def generateFieldWrite(output: jvm.Code, field: ProtoField): List[jvm.Code] = { + val fieldAccess = lang.prop(code"this", naming.grpcFieldName(field.name)) + val unwrapped = field.wrapperType match { + case Some(_) => fieldAccess.callNullary("unwrap") + case None => fieldAccess + } + val fieldNumber = intCode(field.number) + + if (field.isMapField) { + generateMapFieldWrite(output, field, fieldAccess, fieldNumber) + } else if (field.isRepeated) { + generateRepeatedFieldWrite(output, field, fieldAccess, fieldNumber) + } else if (field.proto3Optional || ProtoType.isWrapperType(field.fieldType)) { + generateOptionalFieldWrite(output, field, fieldAccess, fieldNumber) + } else { + val writeStmts = generateScalarFieldWrite(output, field.fieldType, unwrapped, fieldNumber) + field.fieldType match { + case ProtoType.Message(_) => + val body = writeStmts.map(s => code"$s;").mkCode("\n") + List(jvm.If(lang.notEquals(fieldAccess, code"null"), body).code) + case _ => writeStmts + } + } + } + + /** Generate write for a scalar field */ + private def generateScalarFieldWrite(output: jvm.Code, fieldType: ProtoType, value: jvm.Code, fieldNumber: jvm.Code): List[jvm.Code] = { + fieldType match { + case ProtoType.Double => List(output.invoke("writeDouble", fieldNumber, value)) + case ProtoType.Float => List(output.invoke("writeFloat", fieldNumber, value)) + case ProtoType.Int32 => List(output.invoke("writeInt32", fieldNumber, value)) + case ProtoType.Int64 => List(output.invoke("writeInt64", fieldNumber, value)) + case ProtoType.UInt32 => List(output.invoke("writeUInt32", fieldNumber, value)) + case ProtoType.UInt64 => List(output.invoke("writeUInt64", fieldNumber, value)) + case ProtoType.SInt32 => List(output.invoke("writeSInt32", fieldNumber, value)) + case ProtoType.SInt64 => List(output.invoke("writeSInt64", fieldNumber, value)) + case ProtoType.Fixed32 => List(output.invoke("writeFixed32", fieldNumber, value)) + case ProtoType.Fixed64 => List(output.invoke("writeFixed64", fieldNumber, value)) + case ProtoType.SFixed32 => List(output.invoke("writeSFixed32", fieldNumber, value)) + case ProtoType.SFixed64 => List(output.invoke("writeSFixed64", fieldNumber, value)) + case ProtoType.Bool => List(output.invoke("writeBool", fieldNumber, value)) + case ProtoType.String => List(output.invoke("writeString", fieldNumber, value)) + case ProtoType.Bytes => List(output.invoke("writeBytes", fieldNumber, value)) + + case ProtoType.Enum(_) => + List(output.invoke("writeEnum", fieldNumber, value.callNullary("toValue"))) + + case ProtoType.Message(_) => + generateNestedMessageWrite(output, value, fieldNumber) + + case ProtoType.Timestamp => + generateTimestampWrite(output, value, fieldNumber) + + case ProtoType.Duration => + generateDurationWrite(output, value, fieldNumber) + + case wkt if ProtoType.isWrapperType(wkt) => + generateWrapperTypeWrite(output, wkt, value, fieldNumber) + + case _ => List(output.invoke("writeString", fieldNumber, value.invoke("toString"))) + } + } + + /** Generate write for a nested message field: write tag, compute nested size, write length, call nested writeTo */ + private def generateNestedMessageWrite(output: jvm.Code, value: jvm.Code, fieldNumber: jvm.Code): List[jvm.Code] = { + val nestedSize = value.callNullary("getSerializedSize") + List( + output.invoke("writeTag", fieldNumber, code"2"), + output.invoke("writeUInt32NoTag", nestedSize), + value.invoke("writeTo", output) + ) + } + + /** Generate write for Timestamp (seconds=1, nanos=2 as nested message) */ + private def generateTimestampWrite(output: jvm.Code, value: jvm.Code, fieldNumber: jvm.Code): List[jvm.Code] = { + val seconds = value.invoke("getEpochSecond") + val nanos = value.invoke("getNano") + val sizeExpr = code"$CodedOutputStreamType.computeInt64Size(1, $seconds) + $CodedOutputStreamType.computeInt32Size(2, $nanos)" + List( + output.invoke("writeTag", fieldNumber, code"2"), + output.invoke("writeUInt32NoTag", sizeExpr), + output.invoke("writeInt64", code"1", seconds), + output.invoke("writeInt32", code"2", nanos) + ) + } + + /** Generate write for Duration (seconds=1, nanos=2 as nested message) */ + private def generateDurationWrite(output: jvm.Code, value: jvm.Code, fieldNumber: jvm.Code): List[jvm.Code] = { + val seconds = value.invoke("getSeconds") + val nanos = value.invoke("getNano") + val sizeExpr = code"$CodedOutputStreamType.computeInt64Size(1, $seconds) + $CodedOutputStreamType.computeInt32Size(2, $nanos)" + List( + output.invoke("writeTag", fieldNumber, code"2"), + output.invoke("writeUInt32NoTag", sizeExpr), + output.invoke("writeInt64", code"1", seconds), + output.invoke("writeInt32", code"2", nanos) + ) + } + + /** Generate write for well-known wrapper types (nested message with value=1) */ + private def generateWrapperTypeWrite(output: jvm.Code, wkt: ProtoType, value: jvm.Code, fieldNumber: jvm.Code): List[jvm.Code] = { + val underlying = ProtoType.unwrapWellKnown(wkt).getOrElse(sys.error(s"Not a wrapper type: $wkt")) + val innerValue = value.invoke("get") + val computeMethod = computeSizeMethodName(underlying) + val sizeExpr = code"$CodedOutputStreamType.$computeMethod(1, $innerValue)" + val writeMethod = writeMethodName(underlying) + List( + output.invoke("writeTag", fieldNumber, code"2"), + output.invoke("writeUInt32NoTag", sizeExpr), + output.invoke(writeMethod, code"1", innerValue) + ) + } + + /** Generate write for repeated field */ + private def generateRepeatedFieldWrite(output: jvm.Code, field: ProtoField, value: jvm.Code, fieldNumber: jvm.Code): List[jvm.Code] = { + val elem = jvm.Ident("elem") + val elemRef = elem.code + val unwrappedElem = field.wrapperType match { + case Some(_) => elemRef.callNullary("unwrap") + case None => elemRef + } + + val writeStmts = field.fieldType match { + case ProtoType.Message(_) => + generateNestedMessageWrite(output, unwrappedElem, fieldNumber) + case ProtoType.Enum(_) => + List(output.invoke("writeEnum", fieldNumber, unwrappedElem.callNullary("toValue"))) + case ProtoType.Timestamp => + generateTimestampWrite(output, unwrappedElem, fieldNumber) + case ProtoType.Duration => + generateDurationWrite(output, unwrappedElem, fieldNumber) + case _ => + generateScalarFieldWrite(output, field.fieldType, unwrappedElem, fieldNumber) + } + + val elemType = typeMapper.mapFieldType(field.copy(label = ProtoFieldLabel.Optional)) + List(lang.ListType.forEachStmt(value, elem, elemType)(_ => writeStmts)) + } + + /** Generate write for map field */ + private def generateMapFieldWrite(output: jvm.Code, field: ProtoField, value: jvm.Code, fieldNumber: jvm.Code): List[jvm.Code] = { + field.fieldType match { + case ProtoType.Map(keyType, valueType) => + val keyJvmType = typeMapper.mapType(keyType) + val valueJvmType = typeMapper.mapType(valueType) + val keyWriteMethod = writeMethodName(keyType) + val valWriteMethod = writeMethodName(valueType) + + val forEachCode = lang.MapOps.forEachEntry(value, keyJvmType, valueJvmType) { (keyRef, valRef) => + val keySizeExpr = computeSizeExpr(keyType, code"1", keyRef) + val valSizeExpr = computeSizeExpr(valueType, code"2", valRef) + val entrySize = code"$keySizeExpr + $valSizeExpr" + + List( + output.invoke("writeTag", fieldNumber, code"2"), + output.invoke("writeUInt32NoTag", entrySize), + output.invoke(keyWriteMethod, code"1", keyRef), + output.invoke(valWriteMethod, code"2", valRef) + ) + } + List(forEachCode) + case _ => Nil + } + } + + /** Generate write for optional field */ + private def generateOptionalFieldWrite(output: jvm.Code, field: ProtoField, fieldAccess: jvm.Code, fieldNumber: jvm.Code): List[jvm.Code] = { + val v = jvm.Ident("v") + val getV = jvm.LocalVar(v, None, lang.Optional.get(fieldAccess)) + val unwrappedV = field.wrapperType match { + case Some(_) => v.code.callNullary("unwrap") + case None => v.code + } + val writeStmts = field.fieldType match { + case ProtoType.Message(_) => + generateNestedMessageWrite(output, unwrappedV, fieldNumber) + case ProtoType.Enum(_) => + List(output.invoke("writeEnum", fieldNumber, unwrappedV.callNullary("toValue"))) + case ProtoType.Timestamp => + generateTimestampWrite(output, unwrappedV, fieldNumber) + case ProtoType.Duration => + generateDurationWrite(output, unwrappedV, fieldNumber) + case wkt if ProtoType.isWrapperType(wkt) => + val underlying = ProtoType.unwrapWellKnown(wkt).getOrElse(sys.error(s"Not a wrapper type: $wkt")) + val innerValue = unwrappedV + val computeMethod = computeSizeMethodName(underlying) + val sizeExpr = code"$CodedOutputStreamType.$computeMethod(1, $innerValue)" + val writeMethod = writeMethodName(underlying) + List( + output.invoke("writeTag", fieldNumber, code"2"), + output.invoke("writeUInt32NoTag", sizeExpr), + output.invoke(writeMethod, code"1", innerValue) + ) + case _ => + generateScalarFieldWrite(output, field.fieldType, unwrappedV, fieldNumber) + } + val body = (getV.code :: writeStmts).map(s => code"$s;").mkCode("\n") + List(jvm.If(lang.Optional.isDefined(fieldAccess), body).code) + } + + /** Generate oneof write: type-switch on the sealed type, write whichever case is set */ + private def generateOneofWrite(output: jvm.Code, oneof: ProtoOneof, messageFullName: String): List[jvm.Code] = { + val oneofType = naming.grpcOneofTypeName(messageFullName, oneof.name) + val fieldAccess = lang.prop(code"this", naming.grpcFieldName(oneof.name)) + + val cases = oneof.fields.map { field => + val caseName = naming.grpcOneofCaseName(field) + val caseType = oneofType / caseName + val caseIdent = jvm.Ident("c") + val fieldValue = lang.prop(caseIdent.code, naming.grpcFieldName(field.name)) + val unwrapped = field.wrapperType match { + case Some(_) => fieldValue.callNullary("unwrap") + case None => fieldValue + } + val fieldNumber = intCode(field.number) + val writeStmts = field.fieldType match { + case ProtoType.Message(_) => + generateNestedMessageWrite(output, unwrapped, fieldNumber) + case ProtoType.Enum(_) => + List(output.invoke("writeEnum", fieldNumber, unwrapped.callNullary("toValue"))) + case ProtoType.Timestamp => + generateTimestampWrite(output, unwrapped, fieldNumber) + case ProtoType.Duration => + generateDurationWrite(output, unwrapped, fieldNumber) + case _ => + generateScalarFieldWrite(output, field.fieldType, unwrapped, fieldNumber) + } + val body = + if (writeStmts.size == 1) writeStmts.head + else { + val stmtsCode = writeStmts.map(s => code"$s;").mkCode("\n") + code"""|{ + | $stmtsCode + |}""".stripMargin + } + jvm.TypeSwitch.Case(caseType, caseIdent, body) + } + + List(jvm.TypeSwitch(fieldAccess, cases, nullCase = Some(code"{}")).code) + } + + /** Generate getSerializedSize() method */ + private def generateGetSerializedSizeMethod(message: ProtoMessage): jvm.Method = { + val regularFields = message.fields.filter(_.oneofIndex.isEmpty) + + val sizeVar = jvm.Ident("size") + val sizeInit = jvm.MutableVar(sizeVar, Some(lang.Int), code"0") + + val fieldSizeStmts = regularFields.flatMap { field => + generateFieldSizeStmts(sizeVar, field) + } + + val oneofSizeStmts = message.oneofs.flatMap { oneof => + generateOneofSizeStmts(sizeVar, oneof, message.fullName) + } + + val returnStmt = jvm.Return(sizeVar.code).code + + jvm.Method( + annotations = Nil, + comments = jvm.Comments.Empty, + tparams = Nil, + name = jvm.Ident("getSerializedSize"), + params = Nil, + implicitParams = Nil, + tpe = lang.Int, + throws = Nil, + body = jvm.Body.Stmts(List(sizeInit.code) ++ fieldSizeStmts ++ oneofSizeStmts ++ List(returnStmt)), + isOverride = false, + isDefault = false + ) + } + + /** Generate size computation statements for a field */ + private def generateFieldSizeStmts(sizeVar: jvm.Ident, field: ProtoField): List[jvm.Code] = { + val fieldAccess = lang.prop(code"this", naming.grpcFieldName(field.name)) + val unwrapped = field.wrapperType match { + case Some(_) => fieldAccess.callNullary("unwrap") + case None => fieldAccess + } + val fieldNumber = intCode(field.number) + + if (field.isMapField) { + generateMapFieldSizeStmts(sizeVar, field, fieldAccess, fieldNumber) + } else if (field.isRepeated) { + generateRepeatedFieldSizeStmts(sizeVar, field, fieldAccess, fieldNumber) + } else if (field.proto3Optional || ProtoType.isWrapperType(field.fieldType)) { + generateOptionalFieldSizeStmts(sizeVar, field, fieldAccess, fieldNumber) + } else { + val sizeExpr = computeSizeExprForField(field.fieldType, fieldNumber, unwrapped) + val assignStmt = jvm.Assign(sizeVar, code"${sizeVar.code} + $sizeExpr").code + field.fieldType match { + case ProtoType.Message(_) => + List(jvm.If(lang.notEquals(fieldAccess, code"null"), assignStmt).code) + case _ => + List(assignStmt) + } + } + } + + /** Compute size expression for a field type */ + private def computeSizeExprForField(fieldType: ProtoType, fieldNumber: jvm.Code, value: jvm.Code): jvm.Code = { + fieldType match { + case ProtoType.Message(_) => + val nestedSize = value.callNullary("getSerializedSize") + code"$CodedOutputStreamType.computeTagSize($fieldNumber) + $CodedOutputStreamType.computeUInt32SizeNoTag($nestedSize) + $nestedSize" + + case ProtoType.Timestamp => + val seconds = value.invoke("getEpochSecond") + val nanos = value.invoke("getNano") + val innerSize = code"$CodedOutputStreamType.computeInt64Size(1, $seconds) + $CodedOutputStreamType.computeInt32Size(2, $nanos)" + code"$CodedOutputStreamType.computeTagSize($fieldNumber) + $CodedOutputStreamType.computeUInt32SizeNoTag($innerSize) + $innerSize" + + case ProtoType.Duration => + val seconds = value.invoke("getSeconds") + val nanos = value.invoke("getNano") + val innerSize = code"$CodedOutputStreamType.computeInt64Size(1, $seconds) + $CodedOutputStreamType.computeInt32Size(2, $nanos)" + code"$CodedOutputStreamType.computeTagSize($fieldNumber) + $CodedOutputStreamType.computeUInt32SizeNoTag($innerSize) + $innerSize" + + case ProtoType.Enum(_) => + code"$CodedOutputStreamType.computeEnumSize($fieldNumber, ${value.callNullary("toValue")})" + + case wkt if ProtoType.isWrapperType(wkt) => + val underlying = ProtoType.unwrapWellKnown(wkt).getOrElse(sys.error(s"Not a wrapper type: $wkt")) + val innerValue = value.invoke("get") + val innerSize = computeSizeExpr(underlying, code"1", innerValue) + code"$CodedOutputStreamType.computeTagSize($fieldNumber) + $CodedOutputStreamType.computeUInt32SizeNoTag($innerSize) + $innerSize" + + case _ => + computeSizeExpr(fieldType, fieldNumber, value) + } + } + + /** Generate size stmts for repeated field */ + private def generateRepeatedFieldSizeStmts(sizeVar: jvm.Ident, field: ProtoField, value: jvm.Code, fieldNumber: jvm.Code): List[jvm.Code] = { + val elem = jvm.Ident("elem") + val unwrappedElem = field.wrapperType match { + case Some(_) => elem.code.callNullary("unwrap") + case None => elem.code + } + val elemSize = computeSizeExprForField(field.fieldType, fieldNumber, unwrappedElem) + val body = List(jvm.Assign(sizeVar, code"${sizeVar.code} + $elemSize").code) + val elemType = typeMapper.mapFieldType(field.copy(label = ProtoFieldLabel.Optional)) + List(lang.ListType.forEachStmt(value, elem, elemType)(_ => body)) + } + + /** Generate size stmts for map field */ + private def generateMapFieldSizeStmts(sizeVar: jvm.Ident, field: ProtoField, value: jvm.Code, fieldNumber: jvm.Code): List[jvm.Code] = { + field.fieldType match { + case ProtoType.Map(keyType, valueType) => + val keyJvmType = typeMapper.mapType(keyType) + val valueJvmType = typeMapper.mapType(valueType) + val forEachCode = lang.MapOps.forEachEntry(value, keyJvmType, valueJvmType) { (keyRef, valRef) => + val keySizeExpr = computeSizeExpr(keyType, code"1", keyRef) + val valSizeExpr = computeSizeExpr(valueType, code"2", valRef) + val entrySize = code"$keySizeExpr + $valSizeExpr" + val totalEntrySize = code"$CodedOutputStreamType.computeTagSize($fieldNumber) + $CodedOutputStreamType.computeUInt32SizeNoTag($entrySize) + $entrySize" + List(jvm.Assign(sizeVar, code"${sizeVar.code} + $totalEntrySize").code) + } + List(forEachCode) + case _ => Nil + } + } + + /** Generate size stmts for optional field */ + private def generateOptionalFieldSizeStmts(sizeVar: jvm.Ident, field: ProtoField, fieldAccess: jvm.Code, fieldNumber: jvm.Code): List[jvm.Code] = { + val v = jvm.Ident("v") + val getV = jvm.LocalVar(v, None, lang.Optional.get(fieldAccess)) + val unwrappedV = field.wrapperType match { + case Some(_) => v.code.callNullary("unwrap") + case None => v.code + } + val sizeExpr = field.fieldType match { + case wkt if ProtoType.isWrapperType(wkt) => + val underlying = ProtoType.unwrapWellKnown(wkt).getOrElse(sys.error(s"Not a wrapper type: $wkt")) + val innerValue = unwrappedV + val innerSize = computeSizeExpr(underlying, code"1", innerValue) + code"$CodedOutputStreamType.computeTagSize($fieldNumber) + $CodedOutputStreamType.computeUInt32SizeNoTag($innerSize) + $innerSize" + case _ => + computeSizeExprForField(field.fieldType, fieldNumber, unwrappedV) + } + val body = List(getV.code, jvm.Assign(sizeVar, code"${sizeVar.code} + $sizeExpr").code).map(s => code"$s;").mkCode("\n") + List(jvm.If(lang.Optional.isDefined(fieldAccess), body).code) + } + + /** Generate size stmts for oneof */ + private def generateOneofSizeStmts(sizeVar: jvm.Ident, oneof: ProtoOneof, messageFullName: String): List[jvm.Code] = { + val oneofType = naming.grpcOneofTypeName(messageFullName, oneof.name) + val fieldAccess = lang.prop(code"this", naming.grpcFieldName(oneof.name)) + + val cases = oneof.fields.map { field => + val caseName = naming.grpcOneofCaseName(field) + val caseType = oneofType / caseName + val caseIdent = jvm.Ident("c") + val fieldValue = lang.prop(caseIdent.code, naming.grpcFieldName(field.name)) + val unwrapped = field.wrapperType match { + case Some(_) => fieldValue.callNullary("unwrap") + case None => fieldValue + } + val fieldNumber = intCode(field.number) + val sizeExpr = computeSizeExprForField(field.fieldType, fieldNumber, unwrapped) + jvm.TypeSwitch.Case(caseType, caseIdent, jvm.Assign(sizeVar, code"${sizeVar.code} + $sizeExpr").code) + } + + List(jvm.TypeSwitch(fieldAccess, cases, nullCase = Some(code"{}")).code) + } + + /** Generate parseFrom(CodedInputStream) static method */ + private def generateParseFromMethod(message: ProtoMessage, cleanType: jvm.Type.Qualified): jvm.Method = { + val inputParam = jvm.Param(Nil, jvm.Comments.Empty, jvm.Ident("input"), CodedInputStreamType, None) + val input = inputParam.name.code + + val regularFields = message.fields.filter(_.oneofIndex.isEmpty) + + // Declare mutable local variables with default values for each field (reassigned in while loop) + val fieldVarDecls = regularFields.map { field => + val varName = jvm.Ident(naming.grpcFieldName(field.name).value) + val defaultValue = defaultValueForField(field) + val varType = if (field.isRepeated) { + lang.typeSupport.MutableListOps.tpe.of(typeMapper.mapFieldType(field.copy(label = ProtoFieldLabel.Optional))) + } else if (field.isMapField) { + field.fieldType match { + case ProtoType.Map(keyType, valueType) => + lang.MapOps.mutableImpl.of(typeMapper.mapType(keyType), typeMapper.mapType(valueType)) + case _ => typeMapper.mapFieldType(field) + } + } else { + val baseType = typeMapper.mapFieldType(field) + field.fieldType match { + case ProtoType.Message(_) if !field.proto3Optional => lang.nullableRefType(baseType) + case _ => baseType + } + } + jvm.MutableVar(varName, Some(varType), defaultValue) + } + + val oneofVarDecls = message.oneofs.map { oneof => + val varName = jvm.Ident(naming.grpcFieldName(oneof.name).value) + val oneofType = naming.grpcOneofTypeName(message.fullName, oneof.name) + jvm.MutableVar(varName, Some(lang.nullableRefType(oneofType)), code"null") + } + + // Build if-else chain for tag parsing (no Switch in AST, use IfElseChain) + val allFields = regularFields ++ message.oneofs.flatMap(_.fields) + + val tagVar = jvm.Ident("tag") + val tagDecl = jvm.LocalVar(tagVar, None, input.invoke("readTag")) + val fieldNumberExpr = WireFormatType.code.invoke("getTagFieldNumber", tagVar.code) + + val ifCases: List[(jvm.Code, jvm.Code)] = allFields.map { field => + val cond = code"$fieldNumberExpr == ${intCode(field.number)}" + val readStmts = generateFieldRead(input, field, message) + val body = + if (readStmts.size == 1) readStmts.head + else readStmts.map(s => code"$s;").mkCode("\n") + (cond, body) + } + val elseCase = input.invoke("skipField", tagVar.code) + + val dispatchStmt = if (ifCases.nonEmpty) { + jvm.IfElseChain(ifCases, elseCase).code + } else { + elseCase + } + + val whileBody = List(tagDecl.code, dispatchStmt) + val whileLoop = jvm.While(code"!${input.invoke("isAtEnd")}", whileBody) + + // Build constructor call - convert mutable collections to immutable for the record fields + val fieldArgs = regularFields.map { field => + val varRef = jvm.Ident(naming.grpcFieldName(field.name).value).code + val argValue = if (field.isRepeated) { + lang.typeSupport.MutableListOps.toImmutable(varRef) + } else if (field.isMapField) { + field.fieldType match { + case ProtoType.Map(keyType, valueType) => + lang.MapOps.toImmutable(varRef, typeMapper.mapType(keyType), typeMapper.mapType(valueType)) + case _ => varRef + } + } else { + varRef + } + jvm.Arg.Pos(argValue) + } + val oneofArgs = message.oneofs.map { oneof => + jvm.Arg.Pos(jvm.Ident(naming.grpcFieldName(oneof.name).value).code) + } + val constructorCall = jvm.New(cleanType.code, fieldArgs ++ oneofArgs) + val returnStmt = jvm.Return(constructorCall.code).code + + val allStmts = fieldVarDecls.map(_.code) ++ oneofVarDecls.map(_.code) ++ List(whileLoop.code, returnStmt) + + jvm.Method( + annotations = Nil, + comments = jvm.Comments.Empty, + tparams = Nil, + name = jvm.Ident("parseFrom"), + params = List(inputParam), + implicitParams = Nil, + tpe = cleanType, + throws = List(IOException), + body = jvm.Body.Stmts(allStmts), + isOverride = false, + isDefault = false + ) + } + + /** Generate the read expression(s) for a field inside the tag dispatch */ + private def generateFieldRead(input: jvm.Code, field: ProtoField, message: ProtoMessage): List[jvm.Code] = { + val isOneofField = field.oneofIndex.isDefined + val varName = if (isOneofField) { + val oneof = message.oneofs(field.oneofIndex.get) + jvm.Ident(naming.grpcFieldName(oneof.name).value) + } else { + jvm.Ident(naming.grpcFieldName(field.name).value) + } + + if (isOneofField) { + val oneof = message.oneofs(field.oneofIndex.get) + val oneofType = naming.grpcOneofTypeName(message.fullName, oneof.name) + val caseName = naming.grpcOneofCaseName(field) + val caseType = oneofType / caseName + if (isLenDelimitedType(field.fieldType)) { + generateLenDelimitedRead(input, field.fieldType, field.wrapperType) { readValue => + val caseConstructor = jvm.New(caseType.code, List(jvm.Arg.Pos(readValue))) + List(jvm.Assign(varName, caseConstructor.code).code) + } + } else { + val readValue = readScalarValue(input, field.fieldType, field.wrapperType) + val caseConstructor = jvm.New(caseType.code, List(jvm.Arg.Pos(readValue))) + List(jvm.Assign(varName, caseConstructor.code).code) + } + } else if (field.isMapField) { + generateMapFieldRead(input, field, varName) + } else if (field.isRepeated) { + if (isLenDelimitedType(field.fieldType)) { + generateLenDelimitedRead(input, field.fieldType, field.wrapperType) { readValue => + List(lang.typeSupport.MutableListOps.add(varName.code, readValue)) + } + } else { + val readValue = readScalarValue(input, field.fieldType, field.wrapperType) + List(lang.typeSupport.MutableListOps.add(varName.code, readValue)) + } + } else if (field.proto3Optional || ProtoType.isWrapperType(field.fieldType)) { + if (isLenDelimitedType(field.fieldType)) { + generateLenDelimitedRead(input, field.fieldType, field.wrapperType) { readValue => + List(jvm.Assign(varName, lang.Optional.some(readValue)).code) + } + } else { + val readValue = readScalarValue(input, field.fieldType, field.wrapperType) + List(jvm.Assign(varName, lang.Optional.some(readValue)).code) + } + } else { + if (isLenDelimitedType(field.fieldType)) { + generateLenDelimitedRead(input, field.fieldType, field.wrapperType) { readValue => + List(jvm.Assign(varName, readValue).code) + } + } else { + val readValue = readScalarValue(input, field.fieldType, field.wrapperType) + List(jvm.Assign(varName, readValue).code) + } + } + } + + /** Check if a field type requires length-delimited (LEN wire type) parsing with pushLimit/popLimit */ + private def isLenDelimitedType(fieldType: ProtoType): Boolean = fieldType match { + case ProtoType.Message(_) => true + case ProtoType.Timestamp => true + case ProtoType.Duration => true + case wkt if ProtoType.isWrapperType(wkt) => true + case _ => false + } + + /** Generate pushLimit/popLimit wrapper for reading a length-delimited nested message. The `body` function receives the read value expression and returns the assignment/usage statements. + */ + private def generateLenDelimitedRead(input: jvm.Code, fieldType: ProtoType, wrapperType: Option[String])( + body: jvm.Code => List[jvm.Code] + ): List[jvm.Code] = { + val length = jvm.Ident("_length") + val limit = jvm.Ident("_oldLimit") + val lengthDecl = jvm.LocalVar(length, None, input.invoke("readRawVarint32")) + val limitDecl = jvm.LocalVar(limit, None, input.invoke("pushLimit", length.code)) + + val (innerStmts, readValue) = readLenDelimitedValue(input, fieldType, wrapperType) + val popLimit = input.invoke("popLimit", limit.code) + List(lengthDecl.code, limitDecl.code) ++ innerStmts ++ body(readValue) ++ List(popLimit) + } + + /** Read a value from a length-delimited sub-message (after pushLimit is already applied). Returns (extra statements needed before the value, the value expression). + */ + private def readLenDelimitedValue(input: jvm.Code, fieldType: ProtoType, wrapperType: Option[String]): (List[jvm.Code], jvm.Code) = { + val (stmts, raw) = fieldType match { + case ProtoType.Message(fullName) => + val msgType = naming.grpcMessageTypeName(fullName) + (Nil, msgType.code.invoke("parseFrom", input)) + + case ProtoType.Timestamp => + generateTimestampRead(input) + + case ProtoType.Duration => + generateDurationRead(input) + + case wkt if ProtoType.isWrapperType(wkt) => + val underlying = ProtoType.unwrapWellKnown(wkt).getOrElse(sys.error(s"Not a wrapper type: $wkt")) + val skipTag = input.invoke("readTag") + (List(skipTag), readPrimitiveValue(input, underlying)) + + case _ => (Nil, readPrimitiveValue(input, fieldType)) + } + val finalValue = wrapperType match { + case Some(wrapperName) => + val wType = wrapperTypeMap.getOrElse(wrapperName, sys.error(s"Wrapper type $wrapperName not found")) + wType.code.invoke("valueOf", raw) + case None => raw + } + (stmts, finalValue) + } + + /** Generate statements to read a Timestamp from the stream (pushLimit already applied). Returns (statements, value expression). + */ + private def generateTimestampRead(input: jvm.Code): (List[jvm.Code], jvm.Code) = { + val seconds = jvm.Ident("_tsSeconds") + val nanos = jvm.Ident("_tsNanos") + val secondsDecl = jvm.MutableVar(seconds, None, code"0L") + val nanosDecl = jvm.MutableVar(nanos, None, code"0") + + val innerTag = jvm.Ident("_tsTag") + val innerTagDecl = jvm.LocalVar(innerTag, None, input.invoke("readTag")) + val innerFieldNumber = WireFormatType.code.invoke("getTagFieldNumber", innerTag.code) + + val ifChain = jvm.IfElseChain( + List( + (code"$innerFieldNumber == 1", jvm.Assign(seconds, input.invoke("readInt64")).code), + (code"$innerFieldNumber == 2", jvm.Assign(nanos, input.invoke("readInt32")).code) + ), + input.invoke("skipField", innerTag.code) + ) + val whileLoop = jvm.While(code"!${input.invoke("isAtEnd")}", List(innerTagDecl.code, ifChain.code)) + + val InstantType = jvm.Type.Qualified(jvm.QIdent("java.time.Instant")) + val value = InstantType.code.invoke("ofEpochSecond", seconds.code, lang.toLong(nanos.code)) + + (List(secondsDecl.code, nanosDecl.code, whileLoop.code), value) + } + + /** Generate statements to read a Duration from the stream (pushLimit already applied). Returns (statements, value expression). + */ + private def generateDurationRead(input: jvm.Code): (List[jvm.Code], jvm.Code) = { + val seconds = jvm.Ident("_durSeconds") + val nanos = jvm.Ident("_durNanos") + val secondsDecl = jvm.MutableVar(seconds, None, code"0L") + val nanosDecl = jvm.MutableVar(nanos, None, code"0") + + val innerTag = jvm.Ident("_durTag") + val innerTagDecl = jvm.LocalVar(innerTag, None, input.invoke("readTag")) + val innerFieldNumber = WireFormatType.code.invoke("getTagFieldNumber", innerTag.code) + + val ifChain = jvm.IfElseChain( + List( + (code"$innerFieldNumber == 1", jvm.Assign(seconds, input.invoke("readInt64")).code), + (code"$innerFieldNumber == 2", jvm.Assign(nanos, input.invoke("readInt32")).code) + ), + input.invoke("skipField", innerTag.code) + ) + val whileLoop = jvm.While(code"!${input.invoke("isAtEnd")}", List(innerTagDecl.code, ifChain.code)) + + val DurationType = jvm.Type.Qualified(jvm.QIdent("java.time.Duration")) + val value = DurationType.code.invoke("ofSeconds", seconds.code, lang.toLong(nanos.code)) + + (List(secondsDecl.code, nanosDecl.code, whileLoop.code), value) + } + + /** Generate read for a map field entry */ + private def generateMapFieldRead(input: jvm.Code, field: ProtoField, varName: jvm.Ident): List[jvm.Code] = { + field.fieldType match { + case ProtoType.Map(keyType, valueType) => + val keyVar = jvm.Ident("mapKey") + val valVar = jvm.Ident("mapValue") + val keyDefault = defaultValueForType(keyType, None) + val valDefault = defaultValueForType(valueType, None) + val length = jvm.Ident("length") + val limit = jvm.Ident("oldLimit") + + val lengthDecl = jvm.LocalVar(length, None, input.invoke("readRawVarint32")) + val limitDecl = jvm.LocalVar(limit, None, input.invoke("pushLimit", length.code)) + val keyDecl = jvm.MutableVar(keyVar, None, keyDefault) + val valDecl = jvm.MutableVar(valVar, None, valDefault) + + val innerTagVar = jvm.Ident("entryTag") + val innerTagDecl = jvm.LocalVar(innerTagVar, None, input.invoke("readTag")) + val keyRead = readPrimitiveValue(input, keyType) + val valRead = readPrimitiveValue(input, valueType) + val innerFieldNumber = WireFormatType.code.invoke("getTagFieldNumber", innerTagVar.code) + + val innerIfChain = jvm.IfElseChain( + List( + (code"$innerFieldNumber == 1", jvm.Assign(keyVar, keyRead).code), + (code"$innerFieldNumber == 2", jvm.Assign(valVar, valRead).code) + ), + input.invoke("skipField", innerTagVar.code) + ) + val innerWhile = jvm.While(code"!${input.invoke("isAtEnd")}", List(innerTagDecl.code, innerIfChain.code)) + val popLimit = input.invoke("popLimit", limit.code) + val putEntry = lang.MapOps.putVoid(varName.code, keyVar.code, valVar.code) + + List(lengthDecl.code, limitDecl.code, keyDecl.code, valDecl.code, innerWhile.code, popLimit, putEntry) + case _ => Nil + } + } + + /** Read a scalar/message value from input */ + private def readScalarValue(input: jvm.Code, fieldType: ProtoType, wrapperType: Option[String]): jvm.Code = { + val raw = readPrimitiveValue(input, fieldType) + wrapperType match { + case Some(wrapperName) => + val wType = wrapperTypeMap.getOrElse(wrapperName, sys.error(s"Wrapper type $wrapperName not found")) + wType.code.invoke("valueOf", raw) + case None => raw + } + } + + /** Read a primitive/message value from CodedInputStream */ + private def readPrimitiveValue(input: jvm.Code, fieldType: ProtoType): jvm.Code = fieldType match { + case ProtoType.Double => input.invoke("readDouble") + case ProtoType.Float => input.invoke("readFloat") + case ProtoType.Int32 => input.invoke("readInt32") + case ProtoType.Int64 => input.invoke("readInt64") + case ProtoType.UInt32 => input.invoke("readUInt32") + case ProtoType.UInt64 => input.invoke("readUInt64") + case ProtoType.SInt32 => input.invoke("readSInt32") + case ProtoType.SInt64 => input.invoke("readSInt64") + case ProtoType.Fixed32 => input.invoke("readFixed32") + case ProtoType.Fixed64 => input.invoke("readFixed64") + case ProtoType.SFixed32 => input.invoke("readSFixed32") + case ProtoType.SFixed64 => input.invoke("readSFixed64") + case ProtoType.Bool => input.invoke("readBool") + case ProtoType.String => input.invoke("readString") + case ProtoType.Bytes => input.invoke("readBytes") + + case ProtoType.Enum(fullName) => + val enumType = naming.grpcEnumTypeName(fullName) + enumType.code.invoke("fromValue", input.invoke("readEnum")) + + case ProtoType.Message(fullName) => + val msgType = naming.grpcMessageTypeName(fullName) + msgType.code.invoke("parseFrom", input) + + case ProtoType.Timestamp => + val InstantType = jvm.Type.Qualified(jvm.QIdent("java.time.Instant")) + InstantType.code.select("EPOCH") + + case ProtoType.Duration => + val DurationType = jvm.Type.Qualified(jvm.QIdent("java.time.Duration")) + DurationType.code.select("ZERO") + + case wkt if ProtoType.isWrapperType(wkt) => + val underlying = ProtoType.unwrapWellKnown(wkt).getOrElse(sys.error(s"Not a wrapper type: $wkt")) + readPrimitiveValue(input, underlying) + + case _ => input.invoke("readString") + } + + /** Get default value for a field */ + private def defaultValueForField(field: ProtoField): jvm.Code = { + if (field.isRepeated) { + lang.typeSupport.MutableListOps.empty + } else if (field.isMapField) { + field.fieldType match { + case ProtoType.Map(keyType, valueType) => + lang.MapOps.newMutableMap(typeMapper.mapType(keyType), typeMapper.mapType(valueType)) + case _ => lang.MapOps.newMutableMap(lang.topType, lang.topType) + } + } else if (field.proto3Optional || ProtoType.isWrapperType(field.fieldType)) { + lang.Optional.none + } else { + defaultValueForType(field.fieldType, field.wrapperType) + } + } + + /** Get default value for a proto type */ + private def defaultValueForType(protoType: ProtoType, wrapperType: Option[String]): jvm.Code = { + val raw: jvm.Code = protoType match { + case ProtoType.Double => code"0.0" + case ProtoType.Float => code"0.0f" + case ProtoType.Int32 | ProtoType.UInt32 | ProtoType.SInt32 | ProtoType.Fixed32 | ProtoType.SFixed32 => code"0" + case ProtoType.Int64 | ProtoType.UInt64 | ProtoType.SInt64 | ProtoType.Fixed64 | ProtoType.SFixed64 => code"0L" + case ProtoType.Bool => code"false" + case ProtoType.String => jvm.StrLit("").code + case ProtoType.Bytes => jvm.Type.Qualified(jvm.QIdent("com.google.protobuf.ByteString")).code.select("EMPTY") + case ProtoType.Enum(fullName) => naming.grpcEnumTypeName(fullName).code.invoke("fromValue", code"0") + case ProtoType.Message(_) => code"null" + case ProtoType.Timestamp => jvm.Type.Qualified(jvm.QIdent("java.time.Instant")).code.select("EPOCH") + case ProtoType.Duration => jvm.Type.Qualified(jvm.QIdent("java.time.Duration")).code.select("ZERO") + case wkt if ProtoType.isWrapperType(wkt) => lang.Optional.none + case _ => code"null" + } + wrapperType match { + case Some(wrapperName) => + val wType = wrapperTypeMap.getOrElse(wrapperName, sys.error(s"Wrapper type $wrapperName not found")) + wType.code.invoke("valueOf", raw) + case None => raw + } + } + + /** Generate the MARSHALLER static field */ + private def generateMarshallerField(tpe: jvm.Type.Qualified): jvm.ClassMember = { + val marshallerTpe = MarshallerType.of(tpe) + + val valueParam = jvm.Ident("value") + val streamParam = jvm.Ident("stream") + + // stream method: serialize to byte array then wrap in ByteArrayInputStream + val streamMethod = jvm.Method( + annotations = Nil, + comments = jvm.Comments.Empty, + tparams = Nil, + name = jvm.Ident("stream"), + params = List(jvm.Param(Nil, jvm.Comments.Empty, valueParam, tpe, None)), + implicitParams = Nil, + tpe = InputStreamType, + throws = Nil, + body = jvm.Body.Stmts( + List( + jvm.LocalVar(jvm.Ident("bytes"), None, lang.newByteArray(valueParam.code.callNullary("getSerializedSize"))).code, + jvm.LocalVar(jvm.Ident("cos"), None, CodedOutputStreamType.code.invoke("newInstance", jvm.Ident("bytes").code)).code, + jvm + .TryCatch( + tryBlock = List( + valueParam.code.invoke("writeTo", jvm.Ident("cos").code), + jvm.Ident("cos").code.invoke("flush") + ), + catches = List( + jvm.TryCatch.Catch( + exceptionType = IOException, + ident = jvm.Ident("e"), + body = List( + jvm + .Throw( + jvm.Type.Qualified("java.lang.RuntimeException").construct(jvm.Ident("e").code) + ) + .code + ) + ) + ), + finallyBlock = Nil + ) + .code, + jvm.Return(ByteArrayInputStreamType.construct(jvm.Ident("bytes").code)).code + ) + ), + isOverride = true, + isDefault = false + ) + + // parse method: read from InputStream + val parseMethod = jvm.Method( + annotations = Nil, + comments = jvm.Comments.Empty, + tparams = Nil, + name = jvm.Ident("parse"), + params = List(jvm.Param(Nil, jvm.Comments.Empty, streamParam, InputStreamType, None)), + implicitParams = Nil, + tpe = tpe, + throws = Nil, + body = jvm.Body.Stmts( + List( + jvm + .TryCatch( + tryBlock = List( + jvm.Return(tpe.code.invoke("parseFrom", CodedInputStreamType.code.invoke("newInstance", streamParam.code))).code + ), + catches = List( + jvm.TryCatch.Catch( + exceptionType = IOException, + ident = jvm.Ident("e"), + body = List( + jvm + .Throw( + jvm.Type.Qualified("java.lang.RuntimeException").construct(jvm.Ident("e").code) + ) + .code + ) + ) + ), + finallyBlock = Nil + ) + .code + ) + ), + isOverride = true, + isDefault = false + ) + + jvm.Given( + tparams = Nil, + name = naming.grpcMarshallerName, + implicitParams = Nil, + tpe = marshallerTpe, + body = jvm + .NewWithBody( + extendsClass = None, + implementsInterface = Some(marshallerTpe), + members = List(streamMethod, parseMethod) + ) + .code + ) + } + + // ============================================================ + // Helper methods for wire format method names and size computation + // ============================================================ + + /** Get the CodedOutputStream write method name for a proto type */ + private def writeMethodName(protoType: ProtoType): String = protoType match { + case ProtoType.Double => "writeDouble" + case ProtoType.Float => "writeFloat" + case ProtoType.Int32 => "writeInt32" + case ProtoType.Int64 => "writeInt64" + case ProtoType.UInt32 => "writeUInt32" + case ProtoType.UInt64 => "writeUInt64" + case ProtoType.SInt32 => "writeSInt32" + case ProtoType.SInt64 => "writeSInt64" + case ProtoType.Fixed32 => "writeFixed32" + case ProtoType.Fixed64 => "writeFixed64" + case ProtoType.SFixed32 => "writeSFixed32" + case ProtoType.SFixed64 => "writeSFixed64" + case ProtoType.Bool => "writeBool" + case ProtoType.String => "writeString" + case ProtoType.Bytes => "writeBytes" + case ProtoType.Enum(_) => "writeEnum" + case _ => "writeString" + } + + /** Get the CodedOutputStream compute*Size method name for a proto type */ + private def computeSizeMethodName(protoType: ProtoType): String = protoType match { + case ProtoType.Double => "computeDoubleSize" + case ProtoType.Float => "computeFloatSize" + case ProtoType.Int32 => "computeInt32Size" + case ProtoType.Int64 => "computeInt64Size" + case ProtoType.UInt32 => "computeUInt32Size" + case ProtoType.UInt64 => "computeUInt64Size" + case ProtoType.SInt32 => "computeSInt32Size" + case ProtoType.SInt64 => "computeSInt64Size" + case ProtoType.Fixed32 => "computeFixed32Size" + case ProtoType.Fixed64 => "computeFixed64Size" + case ProtoType.SFixed32 => "computeSFixed32Size" + case ProtoType.SFixed64 => "computeSFixed64Size" + case ProtoType.Bool => "computeBoolSize" + case ProtoType.String => "computeStringSize" + case ProtoType.Bytes => "computeBytesSize" + case ProtoType.Enum(_) => "computeEnumSize" + case _ => "computeStringSize" + } + + /** Generate a size computation expression for a scalar field */ + private def computeSizeExpr(protoType: ProtoType, fieldNumber: jvm.Code, value: jvm.Code): jvm.Code = { + val method = computeSizeMethodName(protoType) + code"$CodedOutputStreamType.$method($fieldNumber, $value)" + } +} diff --git a/typr/src/scala/typr/grpc/codegen/OneOfCodegen.scala b/typr/src/scala/typr/grpc/codegen/OneOfCodegen.scala new file mode 100644 index 0000000000..a5d590a213 --- /dev/null +++ b/typr/src/scala/typr/grpc/codegen/OneOfCodegen.scala @@ -0,0 +1,65 @@ +package typr.grpc.codegen + +import typr.grpc._ +import typr.{jvm, Lang, Naming, Scope} +import typr.jvm.Code.{CodeOps, TreeOps} + +/** Generates jvm.File for Protobuf oneof types. + * + * Each oneof becomes a sealed interface/trait. Each field in the oneof becomes a case (record implementing the sealed type). + */ +class OneOfCodegen( + naming: Naming, + typeMapper: ProtobufTypeMapper, + lang: Lang +) { + + /** Generate a sealed type for a oneof group. + * + * @param messageFullName + * Full name of the containing message + * @param oneof + * The oneof definition + * @return + * Generated file for the sealed oneof type + */ + def generate(messageFullName: String, oneof: ProtoOneof): jvm.File = { + val oneofType = naming.grpcOneofTypeName(messageFullName, oneof.name) + + // Generate a case record for each oneof field + val subtypes = oneof.fields.map { field => + val caseName = naming.grpcOneofCaseName(field) + val caseType = oneofType / caseName + val fieldType = typeMapper.mapFieldType(field) + + jvm.Adt.Record( + annotations = Nil, + constructorAnnotations = Nil, + isWrapper = false, + privateConstructor = false, + comments = jvm.Comments.Empty, + name = caseType, + tparams = Nil, + params = List(jvm.Param(Nil, jvm.Comments.Empty, naming.grpcFieldName(field.name), fieldType, None)), + implicitParams = Nil, + `extends` = None, + implements = List(oneofType), + members = Nil, + staticMembers = Nil + ) + } + + val sealedInterface = jvm.Adt.Sum( + annotations = Nil, + comments = jvm.Comments(List(s"OneOf type for ${oneof.name}")), + name = oneofType, + tparams = Nil, + members = Nil, + implements = Nil, + subtypes = subtypes, + staticMembers = Nil + ) + + jvm.File(oneofType, jvm.Code.Tree(sealedInterface), secondaryTypes = Nil, scope = Scope.Main) + } +} diff --git a/typr/src/scala/typr/grpc/codegen/ProtobufTypeMapper.scala b/typr/src/scala/typr/grpc/codegen/ProtobufTypeMapper.scala new file mode 100644 index 0000000000..b5456a342d --- /dev/null +++ b/typr/src/scala/typr/grpc/codegen/ProtobufTypeMapper.scala @@ -0,0 +1,103 @@ +package typr.grpc.codegen + +import typr.grpc._ +import typr.{jvm, Lang, Naming, TypesJava} + +/** Maps Protobuf types to JVM types. + * + * @param lang + * Target language + * @param naming + * Naming conventions + * @param wrapperTypeMap + * Map from wrapperName to generated wrapper types + */ +class ProtobufTypeMapper( + lang: Lang, + naming: Naming, + wrapperTypeMap: Map[String, jvm.Type.Qualified] +) { + + private val ByteStringType = jvm.Type.Qualified(jvm.QIdent("com.google.protobuf.ByteString")) + private val AnyType = jvm.Type.Qualified(jvm.QIdent("com.google.protobuf.Any")) + + def mapType(protoType: ProtoType): jvm.Type = protoType match { + // Scalar types + case ProtoType.Double => lang.Double + case ProtoType.Float => lang.Float + case ProtoType.Int32 => lang.Int + case ProtoType.Int64 => lang.Long + case ProtoType.UInt32 => lang.Int + case ProtoType.UInt64 => lang.Long + case ProtoType.SInt32 => lang.Int + case ProtoType.SInt64 => lang.Long + case ProtoType.Fixed32 => lang.Int + case ProtoType.Fixed64 => lang.Long + case ProtoType.SFixed32 => lang.Int + case ProtoType.SFixed64 => lang.Long + case ProtoType.Bool => lang.Boolean + case ProtoType.String => lang.String + case ProtoType.Bytes => ByteStringType + + // Well-known types + case ProtoType.Timestamp => TypesJava.Instant + case ProtoType.Duration => TypesJava.Duration + + // Well-known wrapper types -> Optional of the underlying scalar + case ProtoType.StringValue => lang.Optional.tpe(lang.String) + case ProtoType.Int32Value => lang.Optional.tpe(lang.Int) + case ProtoType.Int64Value => lang.Optional.tpe(lang.Long) + case ProtoType.UInt32Value => lang.Optional.tpe(lang.Int) + case ProtoType.UInt64Value => lang.Optional.tpe(lang.Long) + case ProtoType.FloatValue => lang.Optional.tpe(lang.Float) + case ProtoType.DoubleValue => lang.Optional.tpe(lang.Double) + case ProtoType.BoolValue => lang.Optional.tpe(lang.Boolean) + case ProtoType.BytesValue => lang.Optional.tpe(ByteStringType) + + // Any -> pass-through + case ProtoType.Any => AnyType + + // Struct -> Map + case ProtoType.Struct => lang.MapOps.tpe.of(lang.String, lang.topType) + + // Empty -> Void + case ProtoType.Empty => jvm.Type.Void + + // Message reference -> generated type + case ProtoType.Message(fullName) => + naming.grpcMessageTypeName(fullName) + + // Enum reference -> generated type + case ProtoType.Enum(fullName) => + naming.grpcEnumTypeName(fullName) + + // Map + case ProtoType.Map(keyType, valueType) => + lang.MapOps.tpe.of(mapType(keyType), mapType(valueType)) + } + + /** Map a field type, respecting wrapper types and optional fields */ + def mapFieldType(field: ProtoField): jvm.Type = { + field.wrapperType match { + case Some(wrapperName) => + val wrapperType = wrapperTypeMap.getOrElse( + wrapperName, + sys.error(s"Wrapper type $wrapperName not found") + ) + if (field.proto3Optional) { + lang.Optional.tpe(wrapperType) + } else { + wrapperType + } + case None => + val baseType = mapType(field.fieldType) + if (field.isRepeated) { + lang.ListType.tpe.of(baseType) + } else if (field.proto3Optional) { + lang.Optional.tpe(baseType) + } else { + baseType + } + } + } +} diff --git a/typr/src/scala/typr/grpc/codegen/ServiceCodegen.scala b/typr/src/scala/typr/grpc/codegen/ServiceCodegen.scala new file mode 100644 index 0000000000..b24d0cd386 --- /dev/null +++ b/typr/src/scala/typr/grpc/codegen/ServiceCodegen.scala @@ -0,0 +1,505 @@ +package typr.grpc.codegen + +import typr.grpc._ +import typr.effects.EffectTypeOps +import typr.{jvm, Lang, Naming, Scope} +import typr.jvm.Code.{CodeOps, TreeOps, TypeOps} +import typr.internal.codegen._ + +/** Generates service interfaces, server adapters, and client wrappers from Protobuf service definitions. + * + * For each service generates: + * 1. Clean service interface - one method per RPC, using clean types 2. Server adapter - implements BindableService, builds ServerServiceDefinition directly 3. Client wrapper - uses Channel + + * ClientCalls with MethodDescriptor constants + */ +class ServiceCodegen( + naming: Naming, + lang: Lang, + options: GrpcOptions, + typeMapper: ProtobufTypeMapper +) { + + private val effectOps: Option[EffectTypeOps] = options.effectType.ops + + // gRPC types + private val IteratorType = lang.typeSupport.JavaIteratorType + private val ChannelType = jvm.Type.Qualified(jvm.QIdent("io.grpc.Channel")) + private val CallOptionsType = jvm.Type.Qualified(jvm.QIdent("io.grpc.CallOptions")) + private val MethodDescriptorType = jvm.Type.Qualified(jvm.QIdent("io.grpc.MethodDescriptor")) + private val MethodTypeType = MethodDescriptorType / jvm.Ident("MethodType") + private val ClientCallsType = jvm.Type.Qualified(jvm.QIdent("io.grpc.stub.ClientCalls")) + private val ServerCallsType = jvm.Type.Qualified(jvm.QIdent("io.grpc.stub.ServerCalls")) + private val ServerServiceDefinitionType = jvm.Type.Qualified(jvm.QIdent("io.grpc.ServerServiceDefinition")) + private val BindableServiceType = jvm.Type.Qualified(jvm.QIdent("io.grpc.BindableService")) + + /** Generate all files for a service */ + def generate(service: ProtoService): List[jvm.File] = { + val files = List.newBuilder[jvm.File] + + // Generate clean service interface + if (options.generateServices) { + files += generateServiceInterface(service) + } + + // Generate server adapter + if (options.generateServers) { + files += generateServerAdapter(service) + } + + // Generate client wrapper + if (options.generateClients) { + files += generateClientWrapper(service) + } + + files.result() + } + + /** Generate the clean service interface with one method per RPC */ + private def generateServiceInterface(service: ProtoService): jvm.File = { + val tpe = naming.grpcServiceTypeName(service.name) + + val methods = service.methods.map { method => + generateServiceMethod(method) + } + + val serviceInterface = jvm.Class( + annotations = Nil, + comments = jvm.Comments(List(s"Clean service interface for ${service.name} gRPC service")), + classType = jvm.ClassType.Interface, + name = tpe, + tparams = Nil, + params = Nil, + implicitParams = Nil, + `extends` = None, + implements = Nil, + members = methods, + staticMembers = Nil + ) + + jvm.File(tpe, jvm.Code.Tree(serviceInterface), secondaryTypes = Nil, scope = Scope.Main) + } + + /** Generate a method signature for a service RPC */ + private def generateServiceMethod(method: ProtoMethod): jvm.Method = { + val methodName = naming.grpcMethodName(method.name) + + val inputType = typeMapper.mapType(ProtoType.Message(method.inputType)) + val outputType = typeMapper.mapType(ProtoType.Message(method.outputType)) + + val (params, returnType) = method.rpcPattern match { + case RpcPattern.Unary => + val p = List(jvm.Param(Nil, jvm.Comments.Empty, jvm.Ident("request"), inputType, None)) + val ret = wrapInEffect(outputType) + (p, ret) + + case RpcPattern.ServerStreaming => + val p = List(jvm.Param(Nil, jvm.Comments.Empty, jvm.Ident("request"), inputType, None)) + val ret = IteratorType.of(outputType) + (p, ret) + + case RpcPattern.ClientStreaming => + val p = List(jvm.Param(Nil, jvm.Comments.Empty, jvm.Ident("requests"), IteratorType.of(inputType), None)) + val ret = wrapInEffect(outputType) + (p, ret) + + case RpcPattern.BidiStreaming => + val p = List(jvm.Param(Nil, jvm.Comments.Empty, jvm.Ident("requests"), IteratorType.of(inputType), None)) + val ret = IteratorType.of(outputType) + (p, ret) + } + + jvm.Method( + annotations = Nil, + comments = jvm.Comments.Empty, + tparams = Nil, + name = methodName, + params = params, + implicitParams = Nil, + tpe = returnType, + throws = Nil, + body = jvm.Body.Abstract, + isOverride = false, + isDefault = false + ) + } + + /** Wrap a type in the configured effect type if non-blocking */ + private def wrapInEffect(tpe: jvm.Type): jvm.Type = { + effectOps match { + case Some(ops) => + val boxed = if (tpe == jvm.Type.Void) { + jvm.Type.Qualified(jvm.QIdent("java.lang.Void")) + } else { + tpe + } + ops.tpe.of(boxed) + case None => tpe + } + } + + /** Get the proto full method name for gRPC: "package.ServiceName/MethodName" */ + private def fullMethodName(service: ProtoService, method: ProtoMethod): String = { + s"${service.fullName}/${method.name}" + } + + /** Get the gRPC MethodType for a method */ + private def grpcMethodType(method: ProtoMethod): jvm.Code = { + method.rpcPattern match { + case RpcPattern.Unary => MethodTypeType.code.select("UNARY") + case RpcPattern.ServerStreaming => MethodTypeType.code.select("SERVER_STREAMING") + case RpcPattern.ClientStreaming => MethodTypeType.code.select("CLIENT_STREAMING") + case RpcPattern.BidiStreaming => MethodTypeType.code.select("BIDI_STREAMING") + } + } + + /** Generate a MethodDescriptor constant for an RPC method */ + private def generateMethodDescriptor(service: ProtoService, method: ProtoMethod): jvm.Value = { + val inputType = naming.grpcMessageTypeName(method.inputType) + val outputType = naming.grpcMessageTypeName(method.outputType) + + val descriptorName = jvm.Ident(methodDescriptorFieldName(method)) + + val inputMarshaller = marshallerRef(inputType) + val outputMarshaller = marshallerRef(outputType) + + val descriptorType = MethodDescriptorType.of(inputType, outputType) + + val builderChain = MethodDescriptorType.code + .invoke("newBuilder", inputMarshaller, outputMarshaller) + .invoke("setType", grpcMethodType(method)) + .invoke("setFullMethodName", jvm.StrLit(fullMethodName(service, method)).code) + .invoke("build") + + jvm.Value( + annotations = Nil, + name = descriptorName, + tpe = descriptorType, + body = Some(builderChain), + isLazy = false, + isOverride = false + ) + } + + /** Generate a reference to a message type's marshaller */ + private def marshallerRef(messageType: jvm.Type.Qualified): jvm.Code = { + messageType.code.select(naming.grpcMarshallerName.value) + } + + /** Generate the field name for a MethodDescriptor constant */ + private def methodDescriptorFieldName(method: ProtoMethod): String = { + // Convert camelCase to UPPER_SNAKE_CASE + val name = method.name + val sb = new StringBuilder + name.foreach { c => + if (c.isUpper && sb.nonEmpty) sb.append('_') + sb.append(c.toUpper) + } + sb.toString() + } + + /** Generate server adapter that implements BindableService and delegates to clean interface */ + private def generateServerAdapter(service: ProtoService): jvm.File = { + val tpe = naming.grpcServerTypeName(service.name) + val serviceTpe = naming.grpcServiceTypeName(service.name) + + val frameworkAnnotations = options.frameworkIntegration.grpcFramework + .map(_.serverAnnotations) + .getOrElse(Nil) + + // Constructor parameter: the clean service implementation + val delegateParam = jvm.Param(Nil, jvm.Comments.Empty, jvm.Ident("delegate"), serviceTpe, None) + + // Generate MethodDescriptor constants as static members + val methodDescriptors = service.methods.map { method => + generateMethodDescriptor(service, method) + } + + // Generate bindService() method + val bindServiceMethod = generateBindServiceMethod(service, tpe, delegateParam.name) + + val serverClass = jvm.Class( + annotations = frameworkAnnotations, + comments = jvm.Comments(List(s"gRPC server adapter for ${service.name} - delegates to clean service interface")), + classType = jvm.ClassType.Class, + name = tpe, + tparams = Nil, + params = List(delegateParam), + implicitParams = Nil, + `extends` = None, + implements = List(BindableServiceType), + members = List(bindServiceMethod), + staticMembers = methodDescriptors + ) + + jvm.File(tpe, jvm.Code.Tree(serverClass), secondaryTypes = Nil, scope = Scope.Main) + } + + /** Generate the bindService() method that builds ServerServiceDefinition */ + private def generateBindServiceMethod(service: ProtoService, classTpe: jvm.Type.Qualified, delegateIdent: jvm.Ident): jvm.Method = { + // Build the ServerServiceDefinition using builder pattern + var builderChain: jvm.Code = ServerServiceDefinitionType.code.invoke("builder", jvm.StrLit(service.fullName).code) + + service.methods.foreach { method => + val descriptorRef = classTpe.code.select(methodDescriptorFieldName(method)) + val handler = generateServerHandler(method, delegateIdent) + + builderChain = builderChain.invoke("addMethod", descriptorRef, handler) + } + + builderChain = builderChain.invoke("build") + + jvm.Method( + annotations = Nil, + comments = jvm.Comments.Empty, + tparams = Nil, + name = jvm.Ident("bindService"), + params = Nil, + implicitParams = Nil, + tpe = ServerServiceDefinitionType, + throws = Nil, + body = jvm.Body.Stmts(List(jvm.Return(builderChain).code)), + isOverride = true, + isDefault = false + ) + } + + /** Generate the ServerCalls handler for a method */ + private def generateServerHandler(method: ProtoMethod, delegateIdent: jvm.Ident): jvm.Code = { + val methodName = naming.grpcMethodName(method.name) + + method.rpcPattern match { + case RpcPattern.Unary => + // ServerCalls.asyncUnaryCall((request, responseObserver) -> { ... }) + val request = jvm.Ident("request") + val responseObserver = jvm.Ident("responseObserver") + val delegateCall = delegateIdent.code.invoke(methodName.value, request.code) + + val lambdaBody = effectOps match { + case Some(ops) => + val response = jvm.Ident("response") + val onItemBody = jvm.Body.Stmts( + List( + responseObserver.code.invoke("onNext", response.code), + responseObserver.code.invoke("onCompleted") + ) + ) + val onItemLambda = jvm.Lambda(List(jvm.LambdaParam(response)), onItemBody) + val error = jvm.Ident("error") + val onFailureLambda = jvm.Lambda(List(jvm.LambdaParam(error)), jvm.Body.Expr(responseObserver.code.invoke("onError", error.code))) + jvm.Body.Stmts(List(ops.subscribeWith(delegateCall, onItemLambda.code, onFailureLambda.code))) + case None => + val onNext = responseObserver.code.invoke("onNext", delegateCall) + val onCompleted = responseObserver.code.invoke("onCompleted") + jvm.Body.Stmts(List(onNext, onCompleted)) + } + + val lambda = jvm.Lambda(List(jvm.LambdaParam(request, None), jvm.LambdaParam(responseObserver, None)), lambdaBody) + ServerCallsType.code.invoke("asyncUnaryCall", lambda.code) + + case RpcPattern.ServerStreaming => + // ServerCalls.asyncServerStreamingCall((request, responseObserver) -> { ... }) + val request = jvm.Ident("request") + val responseObserver = jvm.Ident("responseObserver") + val results = jvm.Ident("results") + val resultsDecl = jvm.LocalVar(results, None, delegateIdent.code.invoke(methodName.value, request.code)) + val whileBody = List( + responseObserver.code.invoke("onNext", results.code.invoke("next")) + ) + val whileLoop = jvm.While(results.code.invoke("hasNext"), whileBody) + val onCompleted = responseObserver.code.invoke("onCompleted") + val lambdaBody = jvm.Body.Stmts(List(resultsDecl.code, whileLoop.code, onCompleted)) + val lambda = jvm.Lambda(List(jvm.LambdaParam(request, None), jvm.LambdaParam(responseObserver, None)), lambdaBody) + ServerCallsType.code.invoke("asyncServerStreamingCall", lambda.code) + + case RpcPattern.ClientStreaming => + val responseObserver = jvm.Ident("responseObserver") + val throwStmt = jvm + .Throw( + jvm.Type + .Qualified("java.lang.UnsupportedOperationException") + .construct(jvm.StrLit("Client streaming not yet implemented in server adapter").code) + ) + .code + val lambda = jvm.Lambda(List(jvm.LambdaParam(responseObserver, None)), jvm.Body.Stmts(List(throwStmt))) + ServerCallsType.code.invoke("asyncClientStreamingCall", lambda.code) + + case RpcPattern.BidiStreaming => + val responseObserver = jvm.Ident("responseObserver") + val throwStmt = jvm + .Throw( + jvm.Type + .Qualified("java.lang.UnsupportedOperationException") + .construct(jvm.StrLit("Bidi streaming not yet implemented in server adapter").code) + ) + .code + val lambda = jvm.Lambda(List(jvm.LambdaParam(responseObserver, None)), jvm.Body.Stmts(List(throwStmt))) + ServerCallsType.code.invoke("asyncBidiStreamingCall", lambda.code) + } + } + + /** Generate client wrapper that uses Channel and ClientCalls directly */ + private def generateClientWrapper(service: ProtoService): jvm.File = { + val tpe = naming.grpcClientTypeName(service.name) + val serviceTpe = naming.grpcServiceTypeName(service.name) + + val frameworkAnnotations = options.frameworkIntegration.grpcFramework + .map(fw => fw.clientFieldAnnotations(service.name)) + .getOrElse(Nil) + + // Constructor parameter: the gRPC Channel + val channelParam = jvm.Param( + frameworkAnnotations, + jvm.Comments.Empty, + jvm.Ident("channel"), + ChannelType, + None + ) + + // Generate MethodDescriptor constants as static members + val methodDescriptors = service.methods.map { method => + generateMethodDescriptor(service, method) + } + + // Generate methods that call stub with proto conversion + val methods = service.methods.map { method => + generateClientMethod(method, tpe, channelParam.name) + } + + val clientClass = jvm.Class( + annotations = Nil, + comments = jvm.Comments(List(s"gRPC client wrapper for ${service.name} - wraps Channel with clean types")), + classType = jvm.ClassType.Class, + name = tpe, + tparams = Nil, + params = List(channelParam), + implicitParams = Nil, + `extends` = None, + implements = List(serviceTpe), + members = methods, + staticMembers = methodDescriptors + ) + + jvm.File(tpe, jvm.Code.Tree(clientClass), secondaryTypes = Nil, scope = Scope.Main) + } + + /** Generate a client method that uses ClientCalls */ + private def generateClientMethod(method: ProtoMethod, classTpe: jvm.Type.Qualified, channelIdent: jvm.Ident): jvm.Method = { + val methodName = naming.grpcMethodName(method.name) + val cleanInputType = naming.grpcMessageTypeName(method.inputType) + val cleanOutputType = naming.grpcMessageTypeName(method.outputType) + val descriptorRef = classTpe.code.select(methodDescriptorFieldName(method)) + + method.rpcPattern match { + case RpcPattern.Unary => + val requestParam = jvm.Param(Nil, jvm.Comments.Empty, jvm.Ident("request"), cleanInputType, None) + val blockingCall = ClientCallsType.code.invoke( + "blockingUnaryCall", + channelIdent.code, + descriptorRef, + CallOptionsType.code.select("DEFAULT"), + requestParam.name.code + ) + + val bodyStmts = effectOps match { + case Some(ops) => + val supplierLambda = jvm.Lambda(Nil, jvm.Body.Expr(blockingCall)) + List(jvm.Return(ops.defer(supplierLambda.code)).code) + case None => + List(jvm.Return(blockingCall).code) + } + + jvm.Method( + annotations = Nil, + comments = jvm.Comments.Empty, + tparams = Nil, + name = methodName, + params = List(requestParam), + implicitParams = Nil, + tpe = wrapInEffect(cleanOutputType), + throws = Nil, + body = jvm.Body.Stmts(bodyStmts), + isOverride = true, + isDefault = false + ) + + case RpcPattern.ServerStreaming => + val requestParam = jvm.Param(Nil, jvm.Comments.Empty, jvm.Ident("request"), cleanInputType, None) + val callExpr = ClientCallsType.code.invoke( + "blockingServerStreamingCall", + channelIdent.code, + descriptorRef, + CallOptionsType.code.select("DEFAULT"), + requestParam.name.code + ) + val returnStmt = jvm.Return(callExpr).code + + jvm.Method( + annotations = Nil, + comments = jvm.Comments.Empty, + tparams = Nil, + name = methodName, + params = List(requestParam), + implicitParams = Nil, + tpe = IteratorType.of(cleanOutputType), + throws = Nil, + body = jvm.Body.Stmts(List(returnStmt)), + isOverride = true, + isDefault = false + ) + + case RpcPattern.ClientStreaming => + val requestsParam = jvm.Param(Nil, jvm.Comments.Empty, jvm.Ident("requests"), IteratorType.of(cleanInputType), None) + + jvm.Method( + annotations = Nil, + comments = jvm.Comments.Empty, + tparams = Nil, + name = methodName, + params = List(requestsParam), + implicitParams = Nil, + tpe = wrapInEffect(cleanOutputType), + throws = Nil, + body = jvm.Body.Stmts( + List( + jvm + .Throw( + jvm.Type + .Qualified("java.lang.UnsupportedOperationException") + .construct(jvm.StrLit("Client streaming not yet implemented in client wrapper").code) + ) + .code + ) + ), + isOverride = true, + isDefault = false + ) + + case RpcPattern.BidiStreaming => + val requestsParam = jvm.Param(Nil, jvm.Comments.Empty, jvm.Ident("requests"), IteratorType.of(cleanInputType), None) + + jvm.Method( + annotations = Nil, + comments = jvm.Comments.Empty, + tparams = Nil, + name = methodName, + params = List(requestsParam), + implicitParams = Nil, + tpe = IteratorType.of(cleanOutputType), + throws = Nil, + body = jvm.Body.Stmts( + List( + jvm + .Throw( + jvm.Type + .Qualified("java.lang.UnsupportedOperationException") + .construct(jvm.StrLit("Bidi streaming not yet implemented in client wrapper").code) + ) + .code + ) + ), + isOverride = true, + isDefault = false + ) + } + } +} diff --git a/typr/src/scala/typr/grpc/parser/GrpcParseError.scala b/typr/src/scala/typr/grpc/parser/GrpcParseError.scala new file mode 100644 index 0000000000..a8cf4fbc28 --- /dev/null +++ b/typr/src/scala/typr/grpc/parser/GrpcParseError.scala @@ -0,0 +1,36 @@ +package typr.grpc.parser + +/** Errors that can occur during Protobuf schema parsing */ +sealed trait GrpcParseError { + def message: String +} + +object GrpcParseError { + case class DirectoryNotFound(path: String) extends GrpcParseError { + def message: String = s"Directory not found: $path" + } + + case class FileReadError(path: String, details: String) extends GrpcParseError { + def message: String = s"Failed to read file $path: $details" + } + + case class ProtocNotFound(details: String) extends GrpcParseError { + def message: String = s"Failed to locate or download protoc: $details" + } + + case class ProtocFailed(exitCode: Int, stderr: String) extends GrpcParseError { + def message: String = s"protoc failed with exit code $exitCode: $stderr" + } + + case class DescriptorParseError(details: String) extends GrpcParseError { + def message: String = s"Failed to parse protobuf descriptor set: $details" + } + + case class MultipleErrors(errors: List[String]) extends GrpcParseError { + def message: String = s"Multiple errors:\n${errors.mkString("\n")}" + } + + case class UnexpectedError(details: String) extends GrpcParseError { + def message: String = s"Unexpected error: $details" + } +} diff --git a/typr/src/scala/typr/grpc/parser/ProtobufParser.scala b/typr/src/scala/typr/grpc/parser/ProtobufParser.scala new file mode 100644 index 0000000000..3d9750c02c --- /dev/null +++ b/typr/src/scala/typr/grpc/parser/ProtobufParser.scala @@ -0,0 +1,535 @@ +package typr.grpc.parser + +import com.google.protobuf.DescriptorProtos.{FieldDescriptorProto, FileDescriptorSet} +import com.google.protobuf.{DescriptorProtos, ExtensionRegistry} +import typr.grpc._ + +import java.io.{BufferedInputStream, FileInputStream} +import java.nio.file.{Files, Path} +import scala.jdk.CollectionConverters._ + +/** Parser for Protobuf schema files (.proto) using protoc and FileDescriptorSet. + * + * Two input modes: + * - ProtoSource.Directory: runs protoc --descriptor_set_out to compile .proto files + * - ProtoSource.DescriptorSet: reads pre-built FileDescriptorSet file + */ +object ProtobufParser { + + /** Parse protos from the configured source */ + def parse(source: ProtoSource): Either[GrpcParseError, List[ProtoFile]] = source match { + case ProtoSource.Directory(path, includePaths) => + parseDirectory(path, includePaths) + case ProtoSource.DescriptorSet(path) => + parseDescriptorSet(path) + } + + /** Parse all .proto files from a directory by running protoc */ + def parseDirectory(directory: Path, includePaths: List[Path]): Either[GrpcParseError, List[ProtoFile]] = { + if (!Files.isDirectory(directory)) { + Left(GrpcParseError.DirectoryNotFound(directory.toString)) + } else { + val protoFiles = Files + .walk(directory) + .filter(p => Files.isRegularFile(p) && p.toString.endsWith(".proto")) + .iterator() + .asScala + .toList + .map(_.toAbsolutePath.normalize()) + .sortBy(_.toString) + + if (protoFiles.isEmpty) { + Right(Nil) + } else { + runProtocAndParse(directory, protoFiles, includePaths) + } + } + } + + /** Parse a pre-built FileDescriptorSet file */ + def parseDescriptorSet(descriptorSetPath: Path): Either[GrpcParseError, List[ProtoFile]] = { + if (!Files.isRegularFile(descriptorSetPath)) { + Left(GrpcParseError.FileReadError(descriptorSetPath.toString, "File not found")) + } else { + readAndConvertDescriptorSet(descriptorSetPath) + } + } + + /** Run protoc to generate a FileDescriptorSet, then parse it */ + private def runProtocAndParse( + sourceDir: Path, + protoFiles: List[Path], + includePaths: List[Path] + ): Either[GrpcParseError, List[ProtoFile]] = { + resolveProtoc().flatMap { protocPath => + val tempFile = Files.createTempFile("typr-grpc-", ".desc") + try { + val allIncludes = (sourceDir :: includePaths).distinct + val includeArgs = allIncludes.flatMap(p => List("--proto_path", p.toString)) + + // Include the typr annotations proto from our resources + val annotationsDir = extractAnnotationsProto() + val annotationsInclude = annotationsDir.map(dir => List("--proto_path", dir.toString)).getOrElse(Nil) + + val args = List(protocPath.toString) ++ + includeArgs ++ + annotationsInclude ++ + List( + "--descriptor_set_out", + tempFile.toString, + "--include_source_info", + "--include_imports" + ) ++ + protoFiles.map(_.toString) + + val process = new ProcessBuilder(args.asJava) + .redirectErrorStream(false) + .start() + + val stderr = new String(process.getErrorStream.readAllBytes()) + val exitCode = process.waitFor() + + if (exitCode != 0) { + Left(GrpcParseError.ProtocFailed(exitCode, stderr)) + } else { + readAndConvertDescriptorSet(tempFile) + } + } finally { + val _ = Files.deleteIfExists(tempFile) + } + } + } + + /** Extract typr/annotations.proto from resources to a temp directory */ + private def extractAnnotationsProto(): Option[Path] = { + val resource = getClass.getResourceAsStream("/typr/annotations.proto") + if (resource == null) { + None + } else { + try { + val tempDir = Files.createTempDirectory("typr-proto-") + val typrDir = tempDir.resolve("typr") + Files.createDirectories(typrDir) + val annotationsFile = typrDir.resolve("annotations.proto") + Files.copy(resource, annotationsFile) + Some(tempDir) + } catch { + case _: Exception => None + } finally { + resource.close() + } + } + } + + /** Resolve protoc binary path. Downloads via coursier if not available. */ + private def resolveProtoc(): Either[GrpcParseError, Path] = { + // Try to find protoc on PATH first + val onPath = findOnPath("protoc") + if (onPath.isDefined) { + Right(onPath.get) + } else { + downloadProtocViaCachingMechanism() + } + } + + /** Find an executable on PATH */ + private def findOnPath(name: String): Option[Path] = { + val pathEnv = System.getenv("PATH") + if (pathEnv == null) return None + + val pathSep = System.getProperty("path.separator") + val exeSuffix = if (System.getProperty("os.name").toLowerCase.contains("win")) ".exe" else "" + + pathEnv + .split(pathSep) + .iterator + .flatMap { dir => + val candidate = Path.of(dir).resolve(name + exeSuffix) + if (Files.isExecutable(candidate)) Some(candidate) else None + } + .nextOption() + } + + /** Download protoc using coursier-style caching. + * + * Downloads from Maven Central: com.google.protobuf:protoc:VERSION:exe:CLASSIFIER + */ + private def downloadProtocViaCachingMechanism(): Either[GrpcParseError, Path] = { + val protocVersion = "4.29.3" + val classifier = osClassifier() + + val cacheDir = Path.of(System.getProperty("user.home"), ".cache", "typr", "protoc", protocVersion) + val exeSuffix = if (System.getProperty("os.name").toLowerCase.contains("win")) ".exe" else "" + val cachedProtoc = cacheDir.resolve(s"protoc$exeSuffix") + + if (Files.isExecutable(cachedProtoc)) { + Right(cachedProtoc) + } else { + try { + Files.createDirectories(cacheDir) + val url = s"https://repo1.maven.org/maven2/com/google/protobuf/protoc/$protocVersion/protoc-$protocVersion-$classifier$exeSuffix" + + val connection = new java.net.URI(url).toURL.openConnection() + val input = new BufferedInputStream(connection.getInputStream) + try { + Files.copy(input, cachedProtoc, java.nio.file.StandardCopyOption.REPLACE_EXISTING) + cachedProtoc.toFile.setExecutable(true) + Right(cachedProtoc) + } finally { + input.close() + } + } catch { + case e: Exception => + Left(GrpcParseError.ProtocNotFound(s"Failed to download protoc: ${e.getMessage}. Install protoc manually or provide a pre-built descriptor set.")) + } + } + } + + /** Detect OS classifier for protoc Maven artifact */ + private def osClassifier(): String = { + val osName = System.getProperty("os.name").toLowerCase + val osArch = System.getProperty("os.arch").toLowerCase + + val os = + if (osName.contains("mac") || osName.contains("darwin")) "osx" + else if (osName.contains("linux")) "linux" + else if (osName.contains("win")) "windows" + else "linux" + + val arch = + if (osArch.contains("aarch64") || osArch.contains("arm64")) "aarch_64" + else if (osArch.contains("x86_64") || osArch.contains("amd64")) "x86_64" + else "x86_64" + + s"$os-$arch" + } + + /** Read and parse a FileDescriptorSet file, converting to our internal types */ + private def readAndConvertDescriptorSet(path: Path): Either[GrpcParseError, List[ProtoFile]] = { + try { + val registry = ExtensionRegistry.newInstance() + registerTyPrExtensions(registry) + + val input = new BufferedInputStream(new FileInputStream(path.toFile)) + val descriptorSet = + try { + FileDescriptorSet.parseFrom(input, registry) + } finally { + input.close() + } + + val protoFiles = descriptorSet.getFileList.asScala.toList + .filterNot(_.getName.startsWith("google/protobuf/")) + .filterNot(_.getName == "typr/annotations.proto") + .map(convertFileDescriptor) + + Right(protoFiles) + } catch { + case e: Exception => + Left(GrpcParseError.DescriptorParseError(e.getMessage)) + } + } + + /** Register typr custom extensions with the ExtensionRegistry. + * + * The typr.wrapper field option (field 50000) is read from unknown fields in extractWrapperOption, so no explicit extension registration is needed. The registry is kept for potential future + * extensions. + */ + private def registerTyPrExtensions(registry: ExtensionRegistry): Unit = {} + + /** Convert a FileDescriptorProto to our internal ProtoFile */ + private def convertFileDescriptor(file: DescriptorProtos.FileDescriptorProto): ProtoFile = { + val protoPackage = if (file.hasPackage) Some(file.getPackage) else None + val javaPackage = if (file.hasOptions && file.getOptions.hasJavaPackage) Some(file.getOptions.getJavaPackage) else None + + val messages = file.getMessageTypeList.asScala.toList + .map(msg => convertMessage(msg, protoPackage.getOrElse(""))) + + val enums = file.getEnumTypeList.asScala.toList + .map(e => convertEnum(e, protoPackage.getOrElse(""))) + + val services = file.getServiceList.asScala.toList + .map(svc => convertService(svc, protoPackage.getOrElse(""))) + + val syntax = if (file.hasSyntax && file.getSyntax == "proto2") ProtoSyntax.Proto2 else ProtoSyntax.Proto3 + + ProtoFile( + protoPackage = protoPackage, + javaPackage = javaPackage, + messages = messages, + enums = enums, + services = services, + sourcePath = if (file.hasName) Some(file.getName) else None, + syntax = syntax + ) + } + + /** Convert a DescriptorProto (message) to our internal ProtoMessage */ + private def convertMessage(msg: DescriptorProtos.DescriptorProto, parentPrefix: String): ProtoMessage = { + val fullName = if (parentPrefix.isEmpty) msg.getName else s"$parentPrefix.${msg.getName}" + + // Build oneofs first so we can assign fields to them + val oneofDescriptors = msg.getOneofDeclList.asScala.toList.zipWithIndex + + val fields = msg.getFieldList.asScala.toList.map { field => + convertField(field, fullName) + } + + // Group fields into oneofs (excluding synthetic oneofs for proto3 optional) + val oneofs = oneofDescriptors.flatMap { case (oneofDesc, idx) => + val oneofFields = fields.filter(_.oneofIndex.contains(idx)) + // Proto3 optional creates synthetic oneofs with no fields (since we set oneofIndex=None for proto3Optional fields) + if (oneofFields.isEmpty) { + None + } else { + Some( + ProtoOneof( + name = oneofDesc.getName, + fields = oneofFields + ) + ) + } + } + + val nestedMessages = msg.getNestedTypeList.asScala.toList + .map(nested => convertMessage(nested, fullName)) + + val nestedEnums = msg.getEnumTypeList.asScala.toList + .map(e => convertEnum(e, fullName)) + + ProtoMessage( + name = msg.getName, + fullName = fullName, + fields = fields, + nestedMessages = nestedMessages, + nestedEnums = nestedEnums, + oneofs = oneofs, + isMapEntry = msg.getOptions.getMapEntry + ) + } + + /** Convert a FieldDescriptorProto to our internal ProtoField */ + private def convertField(field: DescriptorProtos.FieldDescriptorProto, messageFullName: String): ProtoField = { + val fieldType = convertFieldType(field) + val label = convertFieldLabel(field.getLabel) + + // Extract (typr.wrapper) from custom options + val wrapperType = extractWrapperOption(field) + + val proto3Optional = field.hasProto3Optional && field.getProto3Optional + + ProtoField( + name = field.getName, + number = field.getNumber, + fieldType = fieldType, + label = label, + wrapperType = wrapperType, + defaultValue = if (field.hasDefaultValue) Some(field.getDefaultValue) else None, + oneofIndex = if (field.hasOneofIndex && !(proto3Optional)) Some(field.getOneofIndex) else None, + proto3Optional = proto3Optional + ) + } + + /** Convert a field's type descriptor to our internal ProtoType */ + private def convertFieldType(field: DescriptorProtos.FieldDescriptorProto): ProtoType = { + import FieldDescriptorProto.Type._ + + // For message and enum types, check for well-known types first + if (field.getType == TYPE_MESSAGE) { + val typeName = stripLeadingDot(field.getTypeName) + + // Check for well-known types + ProtoType.wellKnownTypes.get(typeName) match { + case Some(wellKnown) => return wellKnown + case None => () + } + + // Check for map entry type (indicated by label REPEATED + message type that is a map entry) + // Maps are handled at a higher level during message conversion + return ProtoType.Message(typeName) + } + + if (field.getType == TYPE_ENUM) { + val typeName = stripLeadingDot(field.getTypeName) + return ProtoType.Enum(typeName) + } + + field.getType match { + case TYPE_DOUBLE => ProtoType.Double + case TYPE_FLOAT => ProtoType.Float + case TYPE_INT64 => ProtoType.Int64 + case TYPE_UINT64 => ProtoType.UInt64 + case TYPE_INT32 => ProtoType.Int32 + case TYPE_FIXED64 => ProtoType.Fixed64 + case TYPE_FIXED32 => ProtoType.Fixed32 + case TYPE_BOOL => ProtoType.Bool + case TYPE_STRING => ProtoType.String + case TYPE_BYTES => ProtoType.Bytes + case TYPE_UINT32 => ProtoType.UInt32 + case TYPE_SFIXED32 => ProtoType.SFixed32 + case TYPE_SFIXED64 => ProtoType.SFixed64 + case TYPE_SINT32 => ProtoType.SInt32 + case TYPE_SINT64 => ProtoType.SInt64 + case _ => ProtoType.Bytes + } + } + + /** Convert field label to our internal representation */ + private def convertFieldLabel(label: FieldDescriptorProto.Label): ProtoFieldLabel = { + import FieldDescriptorProto.Label._ + label match { + case LABEL_OPTIONAL => ProtoFieldLabel.Optional + case LABEL_REQUIRED => ProtoFieldLabel.Required + case LABEL_REPEATED => ProtoFieldLabel.Repeated + case _ => ProtoFieldLabel.Optional + } + } + + /** Extract the (typr.wrapper) option from a field's options. + * + * The field option is registered at field number 50000 as a string extension on FieldOptions. + */ + private def extractWrapperOption(field: DescriptorProtos.FieldDescriptorProto): Option[String] = { + if (!field.hasOptions) return None + + val options = field.getOptions + val unknownFields = options.getUnknownFields + + // The typr.wrapper extension is at field number 50000 + // When parsed with a registry, it appears in the known fields. + // When parsed without a registry, it appears in unknown fields. + val fieldNum = 50000 + + if (unknownFields.hasField(fieldNum)) { + val field50000 = unknownFields.getField(fieldNum) + val values = field50000.getLengthDelimitedList + if (!values.isEmpty) { + Some(values.get(0).toStringUtf8) + } else { + None + } + } else { + None + } + } + + /** Convert a EnumDescriptorProto to our internal ProtoEnum */ + private def convertEnum(enumDesc: DescriptorProtos.EnumDescriptorProto, parentPrefix: String): ProtoEnum = { + val fullName = if (parentPrefix.isEmpty) enumDesc.getName else s"$parentPrefix.${enumDesc.getName}" + + val values = enumDesc.getValueList.asScala.toList.map { v => + ProtoEnumValue(v.getName, v.getNumber) + } + + val allowAlias = enumDesc.getOptions.getAllowAlias + + ProtoEnum( + name = enumDesc.getName, + fullName = fullName, + values = values, + allowAlias = allowAlias + ) + } + + /** Convert a ServiceDescriptorProto to our internal ProtoService */ + private def convertService(svc: DescriptorProtos.ServiceDescriptorProto, parentPrefix: String): ProtoService = { + val fullName = if (parentPrefix.isEmpty) svc.getName else s"$parentPrefix.${svc.getName}" + + val methods = svc.getMethodList.asScala.toList.map { method => + ProtoMethod( + name = method.getName, + inputType = stripLeadingDot(method.getInputType), + outputType = stripLeadingDot(method.getOutputType), + clientStreaming = method.getClientStreaming, + serverStreaming = method.getServerStreaming + ) + } + + ProtoService( + name = svc.getName, + fullName = fullName, + methods = methods + ) + } + + /** Strip leading dot from protobuf fully-qualified type names. + * + * Protobuf uses ".package.MessageName" format in descriptors, but we want "package.MessageName". + */ + private def stripLeadingDot(typeName: String): String = { + if (typeName.startsWith(".")) typeName.substring(1) + else typeName + } + + /** Detect map fields and convert them. + * + * In protobuf, map fields are compiled as repeated MessageType where the message has map_entry option. We need to detect this pattern and convert to ProtoType.Map. + */ + def resolveMapFields(file: ProtoFile): ProtoFile = { + // Collect all map entry types from all messages + def collectMapEntries(messages: List[ProtoMessage]): Map[String, (ProtoType, ProtoType)] = { + messages.flatMap { msg => + // Check nested types for map entries (map entry messages are nested inside the containing message) + val directEntries = msg.nestedMessages.filter(_.isMapEntry).flatMap { entry => + val keyField = entry.fields.find(_.number == 1) + val valueField = entry.fields.find(_.number == 2) + (keyField, valueField) match { + case (Some(k), Some(v)) => + Some(entry.fullName -> (k.fieldType, v.fieldType)) + case _ => None + } + } + directEntries ++ collectMapEntries(msg.nestedMessages) + }.toMap + } + + val allMessages = flattenMessages(file.messages) + val mapEntryTypes = allMessages + .filter(_.isMapEntry) + .flatMap { entry => + val keyField = entry.fields.find(_.number == 1) + val valueField = entry.fields.find(_.number == 2) + (keyField, valueField) match { + case (Some(k), Some(v)) => + Some(entry.fullName -> (k.fieldType, v.fieldType)) + case _ => None + } + } + .toMap + + def resolveMessage(msg: ProtoMessage): ProtoMessage = { + val resolvedFields = msg.fields.map { field => + field.fieldType match { + case ProtoType.Message(typeName) if mapEntryTypes.contains(typeName) => + val (keyType, valueType) = mapEntryTypes(typeName) + field.copy(fieldType = ProtoType.Map(keyType, valueType)) + case _ => field + } + } + + msg.copy( + fields = resolvedFields, + nestedMessages = msg.nestedMessages.filterNot(_.isMapEntry).map(resolveMessage), + oneofs = msg.oneofs.map { oneof => + oneof.copy(fields = oneof.fields.map { field => + field.fieldType match { + case ProtoType.Message(typeName) if mapEntryTypes.contains(typeName) => + val (keyType, valueType) = mapEntryTypes(typeName) + field.copy(fieldType = ProtoType.Map(keyType, valueType)) + case _ => field + } + }) + } + ) + } + + file.copy(messages = file.messages.map(resolveMessage)) + } + + /** Flatten all messages including nested ones */ + private def flattenMessages(messages: List[ProtoMessage]): List[ProtoMessage] = { + messages.flatMap { msg => + msg :: flattenMessages(msg.nestedMessages) + } + } +} diff --git a/typr/src/scala/typr/internal/codegen/FileMariaSet.scala b/typr/src/scala/typr/internal/codegen/FileMariaSet.scala index a195c60c6c..67161b2428 100644 --- a/typr/src/scala/typr/internal/codegen/FileMariaSet.scala +++ b/typr/src/scala/typr/internal/codegen/FileMariaSet.scala @@ -30,6 +30,7 @@ object FileMariaSet { comments, memberEnumType, memberExpressions, + Nil, Nil ) diff --git a/typr/src/scala/typr/internal/codegen/FileStringEnum.scala b/typr/src/scala/typr/internal/codegen/FileStringEnum.scala index 48dc7a8b34..c35808edb7 100644 --- a/typr/src/scala/typr/internal/codegen/FileStringEnum.scala +++ b/typr/src/scala/typr/internal/codegen/FileStringEnum.scala @@ -13,6 +13,6 @@ object FileStringEnum { val comments = scaladoc(s"Enum `${enm.dbEnum.name.value}`" +: enm.members.toList.map { case (_, v) => " - " + v }) val memberExpresions = enm.members.map { case (name, value) => (name, jvm.StrLit(value).code) } - jvm.File(enm.tpe, jvm.Enum(Nil, comments, enm.tpe, memberExpresions, instances), secondaryTypes = Nil, scope = Scope.Main) + jvm.File(enm.tpe, jvm.Enum(Nil, comments, enm.tpe, memberExpresions, Nil, instances), secondaryTypes = Nil, scope = Scope.Main) } } diff --git a/typr/src/scala/typr/internal/codegen/LangJava.scala b/typr/src/scala/typr/internal/codegen/LangJava.scala index 222411d7be..fb86c40320 100644 --- a/typr/src/scala/typr/internal/codegen/LangJava.scala +++ b/typr/src/scala/typr/internal/codegen/LangJava.scala @@ -137,6 +137,7 @@ case object LangJava extends Lang { case jvm.MethodRef(tpe, name) => code"$tpe::$name" case jvm.New(target, args) => code"new $target(${args.map(a => renderTree(a, ctx)).mkCode(", ")})" case jvm.LocalVar(name, tpe, value) => tpe.fold(code"var $name = $value")(t => code"$t $name = $value") + case jvm.MutableVar(name, tpe, value) => tpe.fold(code"var $name = $value")(t => code"$t $name = $value") case jvm.InferredTargs(target) => code"$target<>" case jvm.GenericMethodCall(target, methodName, typeArgs, args) => // Java: target.method(args) @@ -252,7 +253,7 @@ case object LangJava extends Lang { val staticMod = if (ctx.staticImplied) "static " else "" if (tparams.isEmpty && implicitParams.isEmpty) code"""|${annotationsCode}${staticMod}${ctx.public}$tpe $name = - | $body""".stripMargin + | $body;""".stripMargin else { val paramsCode = renderParams(implicitParams, ctx) code"${annotationsCode}${staticMod}${ctx.public}${renderTparams(tparams)} $tpe $name" ++ paramsCode ++ code"""| { @@ -321,6 +322,8 @@ case object LangJava extends Lang { | public static final ${TypesJava.String} Names = ${TypesJava.Arrays}.stream(${enm.tpe}.values()).map(x -> x.value).collect(${TypesJava.Collectors}.joining(", ")); | public static final $enumMap ByName = ${TypesJava.Arrays}.stream(${enm.tpe}.values()).collect(${TypesJava.Collectors}.toMap(n -> n.value, n -> n)); | + | ${enm.members.map(t => code"$t").mkCode("\n")} + | | ${enm.staticMembers.map(t => code"static $t;").mkCode("\n")} | | public static ${enm.tpe.name} force(${TypesJava.String} str) { @@ -807,6 +810,8 @@ case object LangJava extends Lang { override def castFromObject(targetType: jvm.Type, expr: jvm.Code): jvm.Code = code"($targetType) $expr" + override def nullableRefType(tpe: jvm.Type): jvm.Type = tpe + val isKeyword: Set[String] = Set( "abstract", diff --git a/typr/src/scala/typr/internal/codegen/LangKotlin.scala b/typr/src/scala/typr/internal/codegen/LangKotlin.scala index 2b1a15a048..693cef183c 100644 --- a/typr/src/scala/typr/internal/codegen/LangKotlin.scala +++ b/typr/src/scala/typr/internal/codegen/LangKotlin.scala @@ -170,6 +170,8 @@ case class LangKotlin(typeSupport: TypeSupport) extends Lang { override def castFromObject(targetType: jvm.Type, expr: jvm.Code): jvm.Code = code"($expr as $targetType)" + override def nullableRefType(tpe: jvm.Type): jvm.Type = jvm.Type.KotlinNullable(tpe) + val Quote = '"'.toString val TripleQuote = Quote * 3 @@ -254,6 +256,7 @@ case class LangKotlin(typeSupport: TypeSupport) extends Lang { case jvm.MethodRef(tpe, name) => code"$tpe::$name" case jvm.New(target, args) => code"$target(${args.map(a => renderTree(a, ctx)).mkCode(", ")})" case jvm.LocalVar(name, tpe, value) => tpe.fold(code"val $name = $value")(t => code"val $name: $t = $value") + case jvm.MutableVar(name, tpe, value) => tpe.fold(code"var $name = $value")(t => code"var $name: $t = $value") case jvm.InferredTargs(target) => target case jvm.GenericMethodCall(target, methodName, typeArgs, args) => // Kotlin: target.method(args) @@ -357,19 +360,19 @@ case class LangKotlin(typeSupport: TypeSupport) extends Lang { val boundIdent = jvm.Ident("__r") val nullCaseCode = nullCase.map(body => code"null -> $body").toList val typeCases = cases.map { case jvm.TypeSwitch.Case(pat, ident, body) => - // Convert type to wildcard version for pattern matching and cast + // Convert type to wildcard version for pattern matching // Ok -> Ok<*>, Response2004XX5XX -> Response2004XX5XX<*> val wildcardPat = toWildcardType(pat) - // If body starts with {, unwrap it and merge with cast assignment + // Kotlin smart-casts __r to the matched type after `is` check, no explicit `as` needed val bodyStr = body.render(this).asString.trim if (bodyStr.startsWith("{") && bodyStr.endsWith("}")) { val innerBody = bodyStr.drop(1).dropRight(1).trim code"""|is $wildcardPat -> { - | val $ident = $boundIdent as $wildcardPat + | val $ident = $boundIdent | $innerBody |}""".stripMargin } else { - code"is $wildcardPat -> { val $ident = $boundIdent as $wildcardPat; $body }" + code"is $wildcardPat -> { val $ident = $boundIdent; $body }" } } val defaultCaseCode = defaultCase.map(body => code"else -> $body").toList @@ -440,6 +443,8 @@ case class LangKotlin(typeSupport: TypeSupport) extends Lang { code"""|enum class ${enm.tpe.name}(val value: ${TypesKotlin.String}) { | ${enm.values.map { case (name, expr) => code"$name($expr)" }.mkCode(",\n")}; | + | ${enm.members.map(t => code"$t").mkCode("\n")} + | | companion object { | val Names: ${TypesKotlin.String} = entries.joinToString(", ") { it.value } | val ByName: ${TypesKotlin.Map}<${TypesKotlin.String}, ${enm.tpe}> = entries.associateBy { it.value } diff --git a/typr/src/scala/typr/internal/codegen/LangScala.scala b/typr/src/scala/typr/internal/codegen/LangScala.scala index 4cbb36f70c..0c07915f62 100644 --- a/typr/src/scala/typr/internal/codegen/LangScala.scala +++ b/typr/src/scala/typr/internal/codegen/LangScala.scala @@ -187,10 +187,10 @@ case class LangScala(dialect: Dialect, typeSupport: TypeSupport, dsl: DslQualifi code"$tryCode $catchCode $finallyCode" case jvm.IfElseChain(cases, elseCase) => val ifCases = cases.zipWithIndex.map { case ((cond, body), idx) => - if (idx == 0) code"if ($cond) $body" - else code"else if ($cond) $body" + if (idx == 0) code"if ($cond) { $body }" + else code"else if ($cond) { $body }" } - val elseCode = code"else $elseCase" + val elseCode = code"else { $elseCase }" (ifCases :+ elseCode).mkCode("\n") case jvm.Stmt(inner, _) => inner // Scala doesn't need semicolons, just render inner code case jvm.New(target, args) => @@ -200,6 +200,7 @@ case class LangScala(dialect: Dialect, typeSupport: TypeSupport, dsl: DslQualifi |)""".stripMargin else code"new $target(${args.map(a => renderTree(a, ctx)).mkCode(", ")})" case jvm.LocalVar(name, tpe, value) => tpe.fold(code"val $name = $value")(t => code"val $name: $t = $value") + case jvm.MutableVar(name, tpe, value) => tpe.fold(code"var $name = $value")(t => code"var $name: $t = $value") case jvm.InferredTargs(target) => target // Scala doesn't need diamond operator case jvm.GenericMethodCall(target, methodName, typeArgs, args) => // Scala: target.method[T1, T2](args) @@ -304,6 +305,7 @@ case class LangScala(dialect: Dialect, typeSupport: TypeSupport, dsl: DslQualifi code"""|$annotationsCode${renderComments(enm.comments).getOrElse(jvm.Code.Empty)} |enum ${enm.tpe.name} { | case $caseNames + | ${enm.members.map(_.code).mkCode("\n\n")} |} | |object ${enm.tpe.name} { @@ -320,7 +322,9 @@ case class LangScala(dialect: Dialect, typeSupport: TypeSupport, dsl: DslQualifi // Scala 2 sealed abstract class pattern val members = enm.values.map { case (name, expr) => name -> code"case object $name extends ${enm.tpe.name}($expr)" } code"""|$annotationsCode${renderComments(enm.comments).getOrElse(jvm.Code.Empty)} - |sealed abstract class ${enm.tpe.name}(val value: ${TypesJava.String}) + |sealed abstract class ${enm.tpe.name}(val value: ${TypesJava.String}) { + | ${enm.members.map(_.code).mkCode("\n\n")} + |} | |object ${enm.tpe.name} { | ${enm.staticMembers.map(_.code).mkCode("\n\n")} @@ -782,6 +786,8 @@ case class LangScala(dialect: Dialect, typeSupport: TypeSupport, dsl: DslQualifi override def castFromObject(targetType: jvm.Type, expr: jvm.Code): jvm.Code = code"$expr.asInstanceOf[$targetType]" + override def nullableRefType(tpe: jvm.Type): jvm.Type = tpe + override val isKeyword: Set[String] = Set( "abstract", diff --git a/typr/src/scala/typr/internal/codegen/TypeSupportKotlin.scala b/typr/src/scala/typr/internal/codegen/TypeSupportKotlin.scala index 87978b5d42..de4bdc0aac 100644 --- a/typr/src/scala/typr/internal/codegen/TypeSupportKotlin.scala +++ b/typr/src/scala/typr/internal/codegen/TypeSupportKotlin.scala @@ -12,6 +12,7 @@ object TypeSupportKotlin extends TypeSupport { override val Float: jvm.Type.Qualified = TypesKotlin.Float override val Int: jvm.Type.Qualified = TypesKotlin.Int override val IteratorType: jvm.Type.Qualified = TypesKotlin.Iterator + override val JavaIteratorType: jvm.Type.Qualified = TypesKotlin.Iterator override val Long: jvm.Type.Qualified = TypesKotlin.Long override val Short: jvm.Type.Qualified = TypesKotlin.Short override val String: jvm.Type.Qualified = TypesKotlin.String @@ -78,6 +79,9 @@ object TypeSupportKotlin extends TypeSupport { def forEach(collection: jvm.Code, lambda: jvm.Code): jvm.Code = code"$collection.forEach($lambda)" + def forEachStmt(collection: jvm.Code, elem: jvm.Ident, elemType: jvm.Type)(body: jvm.Code => List[jvm.Code]): jvm.Code = + jvm.ForEach(elem, elemType, collection, body(elem.code)).code + def arrayMapToList(array: jvm.Code, mapper: jvm.Code): jvm.Code = code"$array.map($mapper).toList()" @@ -168,6 +172,15 @@ object TypeSupportKotlin extends TypeSupport { // Kotlin map access already returns nullable type def getNullable(map: jvm.Code, key: jvm.Code): jvm.Code = code"$map[$key]" + + def forEachEntry(map: jvm.Code, keyType: jvm.Type, valueType: jvm.Type)(body: (jvm.Code, jvm.Code) => List[jvm.Code]): jvm.Code = { + val key = jvm.Ident("k") + val value = jvm.Ident("v") + val bodyCode = body(key.code, value.code).map(s => code"$s;").mkCode("\n") + code"""|for (($key, $value) in $map) { + | $bodyCode + |}""".stripMargin + } } override object IteratorOps extends IteratorSupport { @@ -196,5 +209,8 @@ object TypeSupportKotlin extends TypeSupport { def add(list: jvm.Code, element: jvm.Code): jvm.Code = code"$list.add($element)" + + def toImmutable(list: jvm.Code): jvm.Code = + list } } diff --git a/typr/src/scala/typr/internal/codegen/addPackageAndImports.scala b/typr/src/scala/typr/internal/codegen/addPackageAndImports.scala index 7f259d2757..c05fa012b9 100644 --- a/typr/src/scala/typr/internal/codegen/addPackageAndImports.scala +++ b/typr/src/scala/typr/internal/codegen/addPackageAndImports.scala @@ -136,12 +136,20 @@ object addPackageAndImports { arg1.mapTrees(t => shortenNames(t, typeImport, staticImport)), arg2.mapTrees(t => shortenNames(t, typeImport, staticImport)) ) - case jvm.Select(target, name) => jvm.Select(target.mapTrees(t => shortenNames(t, typeImport, staticImport)), name) - case jvm.ArrayIndex(target, num) => jvm.ArrayIndex(target.mapTrees(t => shortenNames(t, typeImport, staticImport)), num) - case jvm.ApplyNullary(target, name) => jvm.ApplyNullary(target.mapTrees(t => shortenNames(t, typeImport, staticImport)), name) - case jvm.Arg.Named(name, value) => jvm.Arg.Named(name, value.mapTrees(t => shortenNames(t, typeImport, staticImport))) - case jvm.Arg.Pos(value) => jvm.Arg.Pos(value.mapTrees(t => shortenNames(t, typeImport, staticImport))) - case jvm.Enum(anns, comments, tpe, members, instances) => jvm.Enum(anns, comments, typeImport(tpe), members, instances.map(shortenNamesClassMember(_, typeImport, staticImport))) + case jvm.Select(target, name) => jvm.Select(target.mapTrees(t => shortenNames(t, typeImport, staticImport)), name) + case jvm.ArrayIndex(target, num) => jvm.ArrayIndex(target.mapTrees(t => shortenNames(t, typeImport, staticImport)), num) + case jvm.ApplyNullary(target, name) => jvm.ApplyNullary(target.mapTrees(t => shortenNames(t, typeImport, staticImport)), name) + case jvm.Arg.Named(name, value) => jvm.Arg.Named(name, value.mapTrees(t => shortenNames(t, typeImport, staticImport))) + case jvm.Arg.Pos(value) => jvm.Arg.Pos(value.mapTrees(t => shortenNames(t, typeImport, staticImport))) + case jvm.Enum(anns, comments, tpe, values, members, instances) => + jvm.Enum( + anns, + comments, + typeImport(tpe), + values, + members.map(shortenNamesClassMember(_, typeImport, staticImport)), + instances.map(shortenNamesClassMember(_, typeImport, staticImport)) + ) case jvm.OpenEnum(anns, comments, tpe, underlyingType, values, staticMembers) => jvm.OpenEnum( annotations = anns, @@ -194,6 +202,8 @@ object addPackageAndImports { case x: jvm.Summon => jvm.Summon(shortenNamesType(x.tpe, typeImport)) case jvm.LocalVar(name, tpe, value) => jvm.LocalVar(name, tpe.map(shortenNamesType(_, typeImport)), value.mapTrees(t => shortenNames(t, typeImport, staticImport))) + case jvm.MutableVar(name, tpe, value) => + jvm.MutableVar(name, tpe.map(shortenNamesType(_, typeImport)), value.mapTrees(t => shortenNames(t, typeImport, staticImport))) case jvm.TypeSwitch(value, cases, nullCase, defaultCase, unchecked) => jvm.TypeSwitch( value.mapTrees(t => shortenNames(t, typeImport, staticImport)), diff --git a/typr/src/scala/typr/internal/minimize.scala b/typr/src/scala/typr/internal/minimize.scala index 683d9021ce..5bacc0b139 100644 --- a/typr/src/scala/typr/internal/minimize.scala +++ b/typr/src/scala/typr/internal/minimize.scala @@ -130,6 +130,10 @@ object minimize { goTree(name) tpe.foreach(goTree) go(value) + case jvm.MutableVar(name, tpe, value) => + goTree(name) + tpe.foreach(goTree) + go(value) case jvm.MethodRef(tpe, name) => goTree(tpe) goTree(name) @@ -157,8 +161,9 @@ object minimize { goTree(tpe) throws.foreach(goTree) goBody(body) - case jvm.Enum(_, _, tpe, _, instances) => + case jvm.Enum(_, _, tpe, _, members, instances) => goTree(tpe) + members.foreach(goTree) instances.foreach(goTree) case jvm.Class(_, _, _, _, tparams, params, implicitParams, extends_, implements, members, staticMembers) => tparams.foreach(goTree) diff --git a/typr/src/scala/typr/jvm.scala b/typr/src/scala/typr/jvm.scala index 4e9a9c4636..9772be639d 100644 --- a/typr/src/scala/typr/jvm.scala +++ b/typr/src/scala/typr/jvm.scala @@ -179,6 +179,7 @@ object jvm { comments: Comments, tpe: Type.Qualified, values: NonEmptyList[(Ident, jvm.Code)], + members: List[ClassMember], staticMembers: List[ClassMember] ) extends Tree @@ -428,13 +429,20 @@ object jvm { isImplicit: Boolean = false ) extends ClassMember - /** Local variable declaration - renders as `val` in Scala, `var` in Java */ + /** Local variable declaration - renders as `val` in Scala, `var` in Java/Kotlin */ case class LocalVar( name: Ident, tpe: Option[Type], value: Code ) extends Tree + /** Mutable local variable declaration - renders as `var` in all languages */ + case class MutableVar( + name: Ident, + tpe: Option[Type], + value: Code + ) extends Tree + case class Method( annotations: List[Annotation], comments: Comments, diff --git a/typr/src/scala/typr/openapi/codegen/ModelCodegen.scala b/typr/src/scala/typr/openapi/codegen/ModelCodegen.scala index abdfdaabc2..7bdf00c8c6 100644 --- a/typr/src/scala/typr/openapi/codegen/ModelCodegen.scala +++ b/typr/src/scala/typr/openapi/codegen/ModelCodegen.scala @@ -134,6 +134,7 @@ class ModelCodegen( comments = comments, tpe = tpe, values = values, + members = Nil, staticMembers = staticMembers )