I’m trying to use the errorOut(oneOf
pattern in order to specialize the status code of an endpoint based on an error. My setup is pretty similar to tapir/oneof.md at 855f2d923c8e2d10a0d4af5e49fa33574b529b22 · softwaremill/tapir · GitHub except that I’m using Scala 3’s enum and would like to keep a single json encoder/decoder for the whole ADT:
enum ServerError:
case NotFound(msg: String, entity: String)
case Wrapped(msg: String, underlying: String)
object ServerError:
given decoder: JsonDecoder[ServerError] = DeriveJsonDecoder.gen
given encoder: JsonEncoder[ServerError] = DeriveJsonEncoder.gen
When I try to define my error output as such:
.errorOut(
oneOf[ServerError](
oneOfVariant(statusCode(StatusCode.NotFound).and(jsonBody[NotFound])),
oneOfVariant(statusCode(StatusCode.InternalServerError).and(jsonBody[Wrapped]))
)
)
It can’t find the json encoder for NotFound and Wrapped, which is expected.
How can I used the encoder from ServerError in this case ? I think that a solution could be to use oneOfVariantValueMatcher
with jsonBody[ServerError]
but to match on a specific type each time such as that
oneOf[ServerError](
oneOfVariantValueMatcher(StatusCode.NotFound, jsonBody[ServerError]) { case _: NotFound =>
true
},
oneOfVariantValueMatcher(StatusCode.InternalServerError, jsonBody[ServerError]) { case _: Wrapped =>
true
}
)
That could be extracted into something like that
def customVariantMatcher[T <: ServerError: ClassTag](code: StatusCode) =
oneOfVariantValueMatcher(code, jsonBody[ServerError]) { case _: T => true }
But it feels pretty verbose and I’m sure that there must be another solution