2

I'm toying with Scala for the first time so bear with me. Also using tapir to declare an API, where I'm having issues providing a Schema for an enum.

I have a bunch of enums defined that are part of my domain model and that extend Scala's Enumeration. For instance, this is one of them:

object Status extends Enumeration with JsonEnumeration {
    val Active, Completed, Archived, Deleted = Value
}

And also have many case classes that uses them. For instance, Order uses our previously defined enumeration, like:

case class Order(
    id: String,
    name: Option[String],
    status: Status.Value,
)

I want to make this enum implement a trait that adds an implicit, but without modifying the original Status enumeration (I don't want to couple the Status enum -and all the others- to this trait).

The trait looks like:

import sttp.tapir.{Schema, Validator}

trait TapirEnumeration { e: Enumeration =>
  implicit def schemaForEnum: Schema[e.Value] =
    Schema.string.validate(Validator.enumeration(e.values.toList, v => Option(v)))
}

I wanted to somehow modify the Order object so the Status enum is now a TapirStatus enum (or something like that) which extends both the original Status and TapirEnumeration, but I don't think that can be doable, given that Status is originally defined as a companion object.

Ideally, all the enums I want to expose as responses from my API will implement that TapirEnumeration trait while still extending what they already extend.

What can I do to achieve this? Of course, creating a new enum that implements the trait isn't DRY so it's not an option.

1 Answers1

0

Why does implicit need to be defined in the enum itself in the first place? Just make it its own definition.

import scala.language.implicitConversions
object EnumImplicits {
    implicit def schema[E <: Enumeration](e: E): Schema[e.Value] = ???
}

Then, wherever you need access to that implicit you just make it available with import EnumImplicits._

Here is an example

Dima
  • 39,570
  • 6
  • 44
  • 70
  • Alright, but how can I create a new enum (let's call it ChildStatus) that "extends" the original Status and also adds the import EnumImplicits._ ? – Gabriel Piffaretti Jan 13 '22 at 13:07
  • You don't need a new enum. Just import the implicits at the call site. Then existing enums will just work. – Dima Jan 13 '22 at 13:08
  • Sorry but that didn't work. I get two unused import errors. One when importing EnumImplicits._ where the case class Order is defined (which is the one that has the enums as attributes) and another one inside the EnumImplicits file, saying the implicitConversions import is not being used either. – Gabriel Piffaretti Jan 13 '22 at 14:09
  • unused import is not an error. You probably have something configured badly in whatever is giving you those "errors". `implicitConversions` isn't really needed, you'd just get a different warning if you remove it. But, if it says the other one is unused, you are probably importing it in the wrong place. Hard to tell without seeing the code that actually uses that implicit ... but _that_ is where it needs to be imported (not where the enum is defined). – Dima Jan 13 '22 at 15:57
  • Yeah, I forgot to clarify that I have `-Xfatal-warnings` at my sbt build file (hence the error and not a warning). Also, I read the docs for `implicitConversions` and understood why is needed, but the "unused import" just tells me it's not even used at all. What I think happens here is that the `implicit def schema` function is never being called. Like, just by doing `import EnumImplicits._` that doesn't work. I need to explicitly call the function and assign its result to an implicit val to make it work. – Gabriel Piffaretti Jan 13 '22 at 17:23
  • No, it must simply be confused, because `implicitConversion` is not used in the the code, so it thinks it's redundant. You can remove it, and just enable `-language:implicitConversions` in the build ... though, I would personally just disable fatal-warnings, never saw much point in enabling it to begin with. And no, you don't need to explicitly call a function. That's the whole point of it being implicit. – Dima Jan 13 '22 at 19:57