Skip to content

Commit c24cd23

Browse files
authored
[transcoding] Better message merging syntax/implementation (#59)
1 parent 1fbc2f0 commit c24cd23

File tree

4 files changed

+63
-18
lines changed

4 files changed

+63
-18
lines changed

core/src/main/scala/org/ivovk/connect_rpc_scala/ConnectRouteBuilder.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ import io.grpc.{ManagedChannelBuilder, ServerBuilder, ServerServiceDefinition}
88
import org.http4s.dsl.Http4sDsl
99
import org.http4s.{HttpApp, HttpRoutes, MediaType, Method, Response, Uri}
1010
import org.ivovk.connect_rpc_scala.grpc.*
11+
import org.ivovk.connect_rpc_scala.grpc.MergingBuilder.*
1112
import org.ivovk.connect_rpc_scala.http.*
1213
import org.ivovk.connect_rpc_scala.http.QueryParams.*
1314
import org.ivovk.connect_rpc_scala.http.codec.*
14-
import org.ivovk.connect_rpc_scala.syntax.all.*
1515
import scalapb.{GeneratedMessage as Message, GeneratedMessageCompanion as Companion}
1616

1717
import java.util.concurrent.Executor
@@ -182,7 +182,7 @@ final class ConnectRouteBuilder[F[_] : Async] private(
182182
val queryMessage = jsonCodec.parser.fromJson[Message](queryJson)
183183

184184
transcodingHandler.handleUnary(
185-
bodyMessage.concat(pathMessage, queryMessage),
185+
bodyMessage.merge(pathMessage).merge(queryMessage).build,
186186
req.headers,
187187
method
188188
)
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package org.ivovk.connect_rpc_scala.grpc
2+
3+
import com.google.protobuf.ByteString
4+
import scalapb.{GeneratedMessage as Message, GeneratedMessageCompanion as Companion}
5+
6+
object MergingBuilder {
7+
extension [T <: Message](t: T) {
8+
def merge(other: T)(using Companion[T]): MergingBuilder[T] =
9+
SingleMergingBuilder(t).merge(other)
10+
}
11+
}
12+
13+
sealed trait MergingBuilder[T <: Message] {
14+
def merge(other: T): MergingBuilder[T]
15+
16+
def build: T
17+
}
18+
19+
private class SingleMergingBuilder[T <: Message](t: T)(using cmp: Companion[T]) extends MergingBuilder[T] {
20+
override def merge(other: T): MergingBuilder[T] = {
21+
val empty = cmp.defaultInstance
22+
23+
if other == empty then this
24+
else if t == empty then SingleMergingBuilder(other)
25+
else ListMergingBuilder(other :: t :: Nil)
26+
}
27+
28+
override def build: T = t
29+
}
30+
31+
private class ListMergingBuilder[T <: Message](ts: List[T])(using cmp: Companion[T]) extends MergingBuilder[T] {
32+
override def merge(other: T): MergingBuilder[T] = {
33+
val empty = cmp.defaultInstance
34+
35+
if other == empty then this
36+
else ListMergingBuilder(other :: ts)
37+
}
38+
39+
override def build: T = {
40+
val output = ByteString.newOutput(ts.foldLeft(0)(_ + _.serializedSize))
41+
ts.reverse.foreach(_.writeTo(output))
42+
output.close()
43+
cmp.parseFrom(output.toByteString.newCodedInput())
44+
}
45+
}

core/src/main/scala/org/ivovk/connect_rpc_scala/syntax/all.scala

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
package org.ivovk.connect_rpc_scala.syntax
22

3-
import com.google.protobuf.ByteString
43
import io.grpc.{StatusException, StatusRuntimeException}
54
import org.ivovk.connect_rpc_scala.grpc.GrpcHeaders
6-
import scalapb.{GeneratedMessage, GeneratedMessageCompanion}
5+
import scalapb.GeneratedMessage
76

87
object all extends ExceptionSyntax, ProtoMappingsSyntax
98

@@ -34,20 +33,6 @@ trait ExceptionSyntax {
3433
trait ProtoMappingsSyntax {
3534

3635
extension [T <: GeneratedMessage](t: T) {
37-
def concat(other: T, more: T*): T = {
38-
val cmp = t.companion.asInstanceOf[GeneratedMessageCompanion[T]]
39-
val empty = cmp.defaultInstance
40-
41-
val els = (t :: other :: more.toList).filter(_ != empty)
42-
43-
els match
44-
case Nil => empty
45-
case el :: Nil => el
46-
case _ =>
47-
val is = els.foldLeft(ByteString.empty)(_ concat _.toByteString).newCodedInput()
48-
cmp.parseFrom(is)
49-
}
50-
5136
def toProtoAny: com.google.protobuf.any.Any = {
5237
com.google.protobuf.any.Any(
5338
typeUrl = "type.googleapis.com/" + t.companion.scalaDescriptor.fullName,
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package org.ivovk.connect_rpc_scala.grpc
2+
3+
import org.scalatest.funsuite.AnyFunSuite
4+
import test.HttpCommunicationTest.AddRequest
5+
6+
class MergingBuilderTest extends AnyFunSuite {
7+
import MergingBuilder.*
8+
9+
test("merges three messages") {
10+
val merge = AddRequest(a = 1).merge(AddRequest(b = 2)).merge(AddRequest(a = 3)).build
11+
12+
assert(merge.a == 3)
13+
assert(merge.b == 2)
14+
}
15+
}

0 commit comments

Comments
 (0)