tapir icon indicating copy to clipboard operation
tapir copied to clipboard

Scala 3 enums have limited customisation support

Open johnduffell opened this issue 2 years ago • 5 comments

Following on from https://github.com/softwaremill/tapir/pull/1824#discussion_r913111720

An improvement request for scala 3 enums.

In scala 3 there is an extra way to do enums on top of the existing scala 2 way.

  • if you use traditional scala 2 enums in scala 3 code you still use the old scala.Enumeration.
  • Scala 3 style enums compile to a sealed class that extends scala.reflect.Enum under the covers as per https://docs.scala-lang.org/scala3/reference/enums/enums.html If the enum has no fields the implementations are anonymous, however if the enum has fields then subclasses are created.

As of v1.0.1, using new style enums, as expected the built in derivation will generate a reference to a set of empty objects. However although this makes logical sense if the JSON output is not important, this is not really useful in most cases when working to an existing API spec while modelling things as scala 3 enums.

To see what I mean, follow the instructions in my minimal test case https://github.com/johnduffell/tapir-schema-issue

I can see that it's a customisation to want a different representation, but it's common for me to want to conform to a JSON spec while wanting to model it as a scala enum. So even if we keep the existing behaviour by default, it would be good to hear thoughts on how these use case could be smoother.

I have tried to be as clear as possible - thanks for your time and let me know if you need anything else!

johnduffell avatar Jul 27 '22 15:07 johnduffell

Currently I think automatically deriving schemas for scala3 enums is not possible. This can be solved in two ways:

  • when https://github.com/lampepfl/dotty/discussions/15895 is resolved in some positive way
  • when schemas are directly derived using macros, instead of through magnolia (https://github.com/softwaremill/tapir/issues/2110)

So far I've updated the docs to add a dedicated section on this problem. As a work-around, explicitly specifying the schema for your enum should work:

enum ColorEnum {
   case Green extends ColorEnum
   case Pink extends ColorEnum
}
given Schema[ColorEnum] = Schema.derivedEnumeration(encode = Some(v => v))

adamw avatar Aug 23 '22 13:08 adamw

Hi @adamw, thanks for your response. I have this problem with Scala 3 endpoints. I am using the following code:

  val positionInfoEndpoint: PublicEndpoint[DeviceLocation, Unit, GeolocationServiceResult, Any] =
    endpoint.post
      .in("position-info")
      .in(jsonBody[DeviceLocation])
      .out(jsonBody[GeolocationServiceResult])
[...]
  val docEndpoints: List[ZServerEndpoint[Any, Any]] =
    SwaggerInterpreter().fromEndpoints[Task](
      List(positionInfoEndpoint),
      "zio-geolocation-tapir",
      "1.0.0"
    )

The Scala 3 enum is in the DeviceLocation case class. Where do I have to use the Schema you mentioned above?

longliveenduro avatar Aug 25 '22 13:08 longliveenduro

@longliveenduro the schema is derived when calling jsonBody[...] so I think if you add the implicit before that call (assuming you are using auto-derviation) things should work

Sth like:

  given Schema[ColorEnum] = Schema.derivedEnumeration(encode = Some(v => v))
  val positionInfoEndpoint: PublicEndpoint[DeviceLocation, Unit, GeolocationServiceResult, Any] =
    endpoint.post
      .in("position-info")
      .in(jsonBody[DeviceLocation])
      .out(jsonBody[GeolocationServiceResult])

adamw avatar Aug 25 '22 14:08 adamw

Works like a charm. Thank you very much @adamw !

longliveenduro avatar Aug 25 '22 14:08 longliveenduro

This might be useful? https://github.com/abdolence/circe-tagged-adt-codec-scala3/blob/master/lib/shared/src/main/scala/org/latestbit/circe/adt/codec/impl/JsonTaggedAdtEncoder.scala

Swoorup avatar Sep 09 '22 19:09 Swoorup

Scala 3 enumerations should be now fully customisable, as documented here: https://tapir.softwaremill.com/en/latest/endpoint/enumerations.html

adamw avatar Nov 07 '22 20:11 adamw