Enumeratum

repository·master·Indexed 22 days ago

https://github.com/lloydmeta/enumeratum

A type-safe, performant enumeration implementation for Scala. It provides exhaustive pattern matching warnings, support for ValueEnums (mapping to primitives), and compatibility with ScalaJS and ScalaNative. The library includes extensive integrations for Play Framework, Circe, Slick, Argonaut, Json4s, ReactiveMongo BSON, Quill, and ScalaCheck.

Tokens
13.2K
Snippets
33
Records
60
Agent score
78%

What's inside Enumeratum

  1. Use ValueEnums for primitive mappings

    master

    ValueEnums allow you to map enum members to primitive values like Int, Long, Short, Char, Byte, or String. Unlike standard Enums, ValueEnums enforce compile-time uniqueness of values.

    IntEnum Example

    import enumeratum.values._
    
    sealed abstract class LibraryItem(val value: Int, val name: String) extends IntEnumEntry
    
    object LibraryItem extends IntEnum[LibraryItem] {
      val values = findValues
      case object Book     extends LibraryItem(value = 1, name = "book")
      case object Movie    extends LibraryItem(name = "movie", value = 2)
      case object Magazine extends LibraryItem(3, name = "magazine")
      case object CD       extends LibraryItem(4, name = "cd")
    }
    
    LibraryItem.withValue(1) // LibraryItem.Book

    Allowing Aliases

    If you want multiple entries to share the same value, extend the enumeratum.values.AllowAlias trait.

    sealed abstract class Judgement(val value: Int) extends IntEnumEntry with AllowAlias
    
    object Judgement extends IntEnum[Judgement] {
      case object Good extends Judgement(1)
      case object OK    extends Judgement(2)
      case object Meh   extends Judgement(2)
      case object Bad   extends Judgement(3)
      val values = findValues
    }

    Restrictions:

    • Values must be literal values (not variables).
    • withValue returns an undefined entry if multiple entries share the same value (when using AllowAlias).
    import enumeratum.values._
    
    sealed abstract class LibraryItem(val value: Int, val name: String) extends IntEnumEntry
    
    object LibraryItem extends IntEnum[LibraryItem] {
    
    
      case object Book     extends LibraryItem(value = 1, name = "book")
      case object Movie    extends LibraryItem(name = "movie", value = 2)
      case object Magazine extends LibraryItem(3, name = "magazine")
      case object CD       extends LibraryItem(4, name = "cd")
      // case object Newspaper extends LibraryItem(4, name = "newspaper") <-- will fail to compile because the value 4 is shared
    
      /*
       val five = 5
       case object Article extends LibraryItem(five, name = "article") <-- will fail to compile because the value is not a literal
      */
    
      val values = findValues
    
    }
    
    assert(LibraryItem.withValue(1) == LibraryItem.Book)
    
    LibraryItem.withValue(10) // => java.util.NoSuchElementException:
  2. Integrate Enumeratum with Json4s

    master

    To enable JSON serialization/deserialization for Enumeratum enums using Json4s, add the enumeratum-json4s dependency. You can then use Json4s.serializer(YourEnum) to create an implicit serializer for your enum types.

    // SBT dependency
    libraryDependencies ++= Seq(
        "com.beachape" %% "enumeratum-json4s" % enumeratumJson4sVersion
    )
    
    // Usage
    import enumeratum._
    import org.json4s.DefaultFormats
    
    sealed trait TrafficLight extends EnumEntry
    object TrafficLight extends Enum[TrafficLight] {
      case object Red    extends TrafficLight
      case object Yellow extends TrafficLight
      case object Green  extends TrafficLight
      val values = findValues
    }
    
    // Register the serializer
    implicit val formats = DefaultFormats + Json4s.serializer(TrafficLight)
  3. Install Enumeratum in ScalaJS

    master

    To use Enumeratum in a ScalaJS project, add the following dependency to your build.sbt. Usage in ScalaJS is identical to standard Scala.

    libraryDependencies ++= Seq(
        "com.beachape" %%% "enumeratum" % enumeratumVersion
    )
  4. Override Enum entry names manually or via Mixins

    master

    By default, the name of an enum entry is its toString value. You can change this in two ways:

    1. Manual Override

    Override the entryName method in your EnumEntry implementation.

    sealed abstract class State(override val entryName: String) extends EnumEntry
    
    object State extends Enum[State] {
       val values = findValues
       case object Alabama extends State("AL")
       case object Alaska  extends State("AK")
    }

    2. Using Mixins

    Mix in stackable traits to apply common string formats (e.g., Snakecase, Uppercase, Camelcase, Hyphencase, etc.).

    import enumeratum._
    
    sealed trait Greeting extends EnumEntry with Snakecase
    
    object Greeting extends Enum[Greeting] {
      val values = findValues
      case object Hello        extends Greeting
      case object GoodBye      extends Greeting
      case object ShoutGoodBye extends Greeting with Uppercase
    }
    import enumeratum._
    
    sealed abstract class State(override val entryName: String) extends EnumEntry
    
    object State extends Enum[State] {
    
       val values = findValues
    
       case object Alabama extends State("AL")
       case object Alaska  extends State("AK")
       // and so on and so forth.
    
    }
    
    import State._
    
    State.withName("AL")
  5. Integrate Enumeratum with Slick

    master

    To use Enumeratum enums as columns in Slick tables, use the enumeratum-slick integration.

    Mapping Standard Enums

    Mix in SlickEnumSupport to access mappedColumnTypeForEnum(YourEnum). This maps the enum to a varchar/text column.

    Mapping ValueEnums

    Mix in SlickValueEnumSupport and use mappedColumnTypeForIntEnum(YourEnum) (or other variants) to represent the enum by its underlying numeric value.

    Querying Fixes

    Because enum entries are singleton objects, Slick queries may fail due to type expansion (e.g., TrafficLight.Red being inferred as its specific subtype rather than TrafficLight).

    Solution 1: Type Ascription

    .filter(_.trafficLight === (TrafficLight.Red: TrafficLight))

    Solution 2: Typed Accessors Define typed accessors in your enum companion object:

    object TrafficLight extends Enum[TrafficLight] {
      val red: TrafficLight = Red
      // ...
    }
    // Use in query:
    .filter(_.trafficLight === red)

    Plain SQL Support

    For interpolated or plain SQL, import SlickEnumPlainSqlSupport._ and define GetResult, SetParameter, and their Option variants using getResultForEnum, setParameterForEnum, etc.

    // Mapping standard Enum
    trait GreetingRepository extends SlickEnumSupport {
      val profile: slick.jdbc.Profile
      implicit lazy val greetingMapper = mappedColumnTypeForEnum(Greeting)
      class GreetingTable(tag: Tag) extends Table[(String, Greeting)](tag, "greeting") {
        def id = column[String]("id", O.PrimaryKey)
        def greeting = column[Greeting]("greeting") // Maps to varchar/text
        def * = (id, greeting)
      }
    }
    
    // Mapping ValueEnum (numeric)
    implicit lazy val libraryItemMapper = mappedColumnTypeForIntEnum(LibraryItem)
    // ... maps to a numeric column
  6. Integrate Enumeratum with Play JSON

    master

    The enumeratum-play-json module provides auto-generated boilerplate for JSON serialization in your Enums using Play's JSON library.

    Installation:

    libraryDependencies ++= Seq(
        "com.beachape" %% "enumeratum-play-json" % enumeratumPlayJsonVersion
    )

    Available Traits:

    • PlayJsonEnum[T]
    • PlayInsensitiveJsonEnum[T]
    • PlayLowercaseJsonEnum[T]
    • PlayUppercaseJsonEnum[T]
    • For ValueEnums: IntPlayJsonValueEnum[T], LongPlayJsonValueEnum[T], and ShortPlayJsonValueEnum[T].
  7. Run JMH Benchmarks

    master

    Benchmarks are located in the benchmarking project. You can run them using the following command in your terminal via sbt:

    sbt +benchmarking/'jmh:run -i 10 -wi 10 -f3 -t 1'

    To run against the main/latest supported version of Scala, remove the + prefix.

    sbt +benchmarking/'jmh:run -i 10 -wi 10 -f3 -t 1'
  8. Integrate Enumeratum with Doobie

    master

    To use Enumeratum with the Doobie functional JDBC layer, add the enumeratum-doobie dependency to your project. This allows you to map Enumeratum enums directly to database columns.

    For JVM projects, use %%. For ScalaJS projects, use %%%.

    // JVM
    libraryDependencies ++= Seq(
        "com.beachape" %% "enumeratum-doobie" % enumeratumDoobieVersion
    )
    
    // ScalaJS
    libraryDependencies ++= Seq(
        "com.beachape" %%% "enumeratum-doobie" % enumeratumDoobieVersion
    )
  9. Integrate Enumeratum with Cats

    master

    The enumeratum-cats module provides type-class instances for Eq, Show, and Hash for your enums, which is useful for generic derivation in case classes.

    Implementation Types

    • CatsEnum[T]: Provides Eq, Show, and Hash for standard Enum types.
    • CatsValueEnum[T]: Provides Eq and Show for ValueEnum types.
    • CatsOrderValueEnum[V, T]: Provides Eq, Show, and cats.Order for ValueEnum types (requires an abstract class implementation due to Scala 2 limitations).

    Inheritance-free usage

    If you prefer not to mix in traits, you can use the helper methods in enumeratum.Cats and enumeratum.values.Cats to obtain instances.

    // SBT dependency (JVM)
    libraryDependencies ++= Seq(
        "com.beachape" %% "enumeratum-cats" % enumeratumCatsVersion
    )
    
    // SBT dependency (ScalaJS)
    libraryDependencies ++= Seq(
        "com.beachape" %%% "enumeratum-cats" % enumeratumCatsVersion
    )
    
    // Usage with CatsOrderValueEnum
    import enumeratum.values._
    import cats.syntax.order._
    
    sealed abstract class CatsPriority(val value: Int, val name: String) extends IntEnumEntry
    
    case object CatsPriority extends IntEnum[CatsPriority] with CatsOrderValueEnum[Int, CatsPriority] {
      case object Low         extends CatsPriority(value = 1, name = "low")
      case object Medium      extends CatsPriority(name = "medium", value = 2)
      case object High        extends CatsPriority(3, "high")
      case object SuperHigh   extends CatsPriority(4, "super_high")
      val values = findValues
    }
    
    val items: List[CatsPriority] = List(High, Low, SuperHigh)
    items.maximumOption // Some(SuperHigh)
  10. Create a basic Enum

    master

    To define an enumeration, create a sealed trait that extends EnumEntry, and an accompanying object that extends Enum[T]. Use the findValues macro to automatically populate the values member with all declared case objects.

    import enumeratum._
    
    sealed trait Greeting extends EnumEntry
    
    object Greeting extends Enum[Greeting] {
      val values = findValues
    
      case object Hello   extends Greeting
      case object GoodBye extends Greeting
      case object Hi      extends Greeting
      case object Bye     extends Greeting
    }
    import enumeratum._
    
    sealed trait Greeting extends EnumEntry
    
    object Greeting extends Enum[Greeting] {
    
      /*
       `findValues` is a protected method that invokes a macro to find all `Greeting` object declarations inside an `Enum`
    
       You use it to implement the `val values` member
      */
      val values = findValues
    
      case object Hello   extends Greeting
      case object GoodBye extends Greeting
      case object Hi      extends Greeting
      case object Bye     extends Greeting
    
    }
  11. Integrate Enumeratum with Quill

    master

    To use Enumeratum with Quill for database persistence, add the enumeratum-quill dependency. For ScalaJS support, use the triple percent %%% operator.

    Usage Requirements

    • Type Ascription: When using lift with hardcoded EnumEntry or ValueEnumEntry values in queries, you must ascribe the type (e.g., lift(ShirtSize.Small: ShirtSize)) to ensure correct binding.
    • Compatibility:
      • quill-cassandra does not support ShortEnum or ByteEnum.
      • quill-orientdb does not support ByteEnum.
    // SBT dependency (JVM)
    libraryDependencies ++= Seq(
        "com.beachape" %% "enumeratum-quill" % enumeratumQuillVersion
    )
    
    // SBT dependency (ScalaJS)
    libraryDependencies ++= Seq(
        "com.beachape" %%% "enumeratum-quill" % enumeratumQuillVersion
    )
    
    // Usage Example
    import enumeratum._
    import io.getquill._
    
    sealed trait ShirtSize extends EnumEntry
    case object ShirtSize extends Enum[ShirtSize] with QuillEnum[ShirtSize] {
      case object Small  extends ShirtSize
      case object Medium extends ShirtSize
      case object Large  extends ShirtSize
      val values = findValues
    }
    
    case class Shirt(size: ShirtSize)
    
    lazy val ctx = new PostgresJdbcContext(SnakeCase, "ctx")
    import ctx._
    
    // Note the type ascription: : ShirtSize
    ctx.run(query[Shirt].insert(_.size -> lift(ShirtSize.Small: ShirtSize)))