diff --git a/codegen/src/main/scala/fs2/grpc/codegen/Fs2GrpcServicePrinter.scala b/codegen/src/main/scala/fs2/grpc/codegen/Fs2GrpcServicePrinter.scala index 0f536c70..e7aeede6 100644 --- a/codegen/src/main/scala/fs2/grpc/codegen/Fs2GrpcServicePrinter.scala +++ b/codegen/src/main/scala/fs2/grpc/codegen/Fs2GrpcServicePrinter.scala @@ -91,6 +91,18 @@ class Fs2GrpcServicePrinter(service: ServiceDescriptor, serviceSuffix: String, d p.add(s".addMethod($descriptor, $handler((r, m) => $eval.flatMap($serviceCall(r, _))))") } + private[this] def serviceBindingResourceImplementation(method: MethodDescriptor): PrinterEndo = { p => + val inType = method.inputType.scalaType + val outType = method.outputType.scalaType + val descriptor = method.grpcDescriptor.fullName + val handler = s"$Fs2ServerCallHandler[F](dispatcher, serverOptions).${handleMethod(method)}[$inType, $outType]" + + val serviceCall = s"serviceImpl.${method.name}" + val eval = if (method.isServerStreaming) s"$Stream.resource[F, A](mkCtx(m)).flatMap" else "mkCtx(m).use" + + p.add(s".addMethod($descriptor, $handler((r, m) => $eval($serviceCall(r, _))))") + } + private[this] def serviceMethods: PrinterEndo = _.seq(service.methods.map(serviceMethodSignature)) private[this] def serviceMethodImplementations: PrinterEndo = @@ -103,6 +115,13 @@ class Fs2GrpcServicePrinter(service: ServiceDescriptor, serviceSuffix: String, d .add(".build()") .outdent + private[this] def serviceBindingResourceImplementations: PrinterEndo = + _.indent + .add(s".builder(${service.grpcDescriptor.fullName})") + .call(service.methods.map(serviceBindingResourceImplementation): _*) + .add(".build()") + .outdent + private[this] def serviceTrait: PrinterEndo = _.add(s"trait $serviceNameFs2[F[_], $Ctx] {").indent.call(serviceMethods).outdent.add("}") @@ -111,6 +130,8 @@ class Fs2GrpcServicePrinter(service: ServiceDescriptor, serviceSuffix: String, d .call(serviceClient) .newline .call(serviceBinding) + .newline + .call(serviceBindingResource) .outdent .newline .add("}") @@ -134,6 +155,16 @@ class Fs2GrpcServicePrinter(service: ServiceDescriptor, serviceSuffix: String, d .add("}") } + private[this] def serviceBindingResource: PrinterEndo = { + _.add( + s"protected def serviceBindingResource[F[_]: $Async, $Ctx](dispatcher: $Dispatcher[F], serviceImpl: $serviceNameFs2[F, $Ctx], mkCtx: $Metadata => $Resource[F, $Ctx], serverOptions: $ServerOptions): $ServerServiceDefinition = {" + ).indent + .add(s"$ServerServiceDefinition") + .call(serviceBindingResourceImplementations) + .outdent + .add("}") + } + // / def printService(printer: FunctionalPrinter): FunctionalPrinter = { diff --git a/e2e/src/test/resources/TestServiceFs2Grpc.scala.txt b/e2e/src/test/resources/TestServiceFs2Grpc.scala.txt index 3c4b5add..dbf9091c 100644 --- a/e2e/src/test/resources/TestServiceFs2Grpc.scala.txt +++ b/e2e/src/test/resources/TestServiceFs2Grpc.scala.txt @@ -43,5 +43,15 @@ object TestServiceFs2Grpc extends _root_.fs2.grpc.GeneratedCompanion[TestService .addMethod(hello.world.TestServiceGrpc.METHOD_BOTH_STREAMING, _root_.fs2.grpc.server.Fs2ServerCallHandler[F](dispatcher, serverOptions).streamingToStreamingCall[hello.world.TestMessage, hello.world.TestMessage]((r, m) => _root_.fs2.Stream.eval(mkCtx(m)).flatMap(serviceImpl.bothStreaming(r, _)))) .build() } + + protected def serviceBindingResource[F[_]: _root_.cats.effect.Async, A](dispatcher: _root_.cats.effect.std.Dispatcher[F], serviceImpl: TestServiceFs2Grpc[F, A], mkCtx: _root_.io.grpc.Metadata => _root_.cats.effect.Resource[F, A], serverOptions: _root_.fs2.grpc.server.ServerOptions): _root_.io.grpc.ServerServiceDefinition = { + _root_.io.grpc.ServerServiceDefinition + .builder(hello.world.TestServiceGrpc.SERVICE) + .addMethod(hello.world.TestServiceGrpc.METHOD_NO_STREAMING, _root_.fs2.grpc.server.Fs2ServerCallHandler[F](dispatcher, serverOptions).unaryToUnaryCall[hello.world.TestMessage, hello.world.TestMessage]((r, m) => mkCtx(m).use(serviceImpl.noStreaming(r, _)))) + .addMethod(hello.world.TestServiceGrpc.METHOD_CLIENT_STREAMING, _root_.fs2.grpc.server.Fs2ServerCallHandler[F](dispatcher, serverOptions).streamingToUnaryCall[hello.world.TestMessage, hello.world.TestMessage]((r, m) => mkCtx(m).use(serviceImpl.clientStreaming(r, _)))) + .addMethod(hello.world.TestServiceGrpc.METHOD_SERVER_STREAMING, _root_.fs2.grpc.server.Fs2ServerCallHandler[F](dispatcher, serverOptions).unaryToStreamingCall[hello.world.TestMessage, hello.world.TestMessage]((r, m) => _root_.fs2.Stream.resource[F, A](mkCtx(m)).flatMap(serviceImpl.serverStreaming(r, _)))) + .addMethod(hello.world.TestServiceGrpc.METHOD_BOTH_STREAMING, _root_.fs2.grpc.server.Fs2ServerCallHandler[F](dispatcher, serverOptions).streamingToStreamingCall[hello.world.TestMessage, hello.world.TestMessage]((r, m) => _root_.fs2.Stream.resource[F, A](mkCtx(m)).flatMap(serviceImpl.bothStreaming(r, _)))) + .build() + } } \ No newline at end of file diff --git a/runtime/src/main/scala/fs2/grpc/GeneratedCompanion.scala b/runtime/src/main/scala/fs2/grpc/GeneratedCompanion.scala index b1499bed..2b4d8651 100644 --- a/runtime/src/main/scala/fs2/grpc/GeneratedCompanion.scala +++ b/runtime/src/main/scala/fs2/grpc/GeneratedCompanion.scala @@ -123,6 +123,13 @@ trait GeneratedCompanion[Service[*[_], _]] { serverOptions: ServerOptions ): ServerServiceDefinition + protected def serviceBindingResource[F[_]: Async, A]( + dispatcher: Dispatcher[F], + serviceImpl: Service[F, A], + mkCtx: Metadata => Resource[F, A], + serverOptions: ServerOptions + ): ServerServiceDefinition + final def service[F[_]: Async, A]( dispatcher: Dispatcher[F], serviceImpl: Service[F, A], @@ -139,6 +146,13 @@ trait GeneratedCompanion[Service[*[_], _]] { serviceBinding[F, A](dispatcher, serviceImpl, mkCtx, serverOptions) } + final def service[F[_]: Async, A]( + serviceImpl: Service[F, A], + f: Metadata => Resource[F, A], + serverOptions: ServerOptions + ): Resource[F, ServerServiceDefinition] = + Dispatcher[F].map(serviceBindingResource[F, A](_, serviceImpl, f, serverOptions)) + final def service[F[_]: Async, A]( dispatcher: Dispatcher[F], serviceImpl: Service[F, A],