Skip to content

Commit

Permalink
Circe KeyEncoder/KeyDecoder for String Enum (#178)
Browse files Browse the repository at this point in the history
* Enable Circe string enums to be used as keys of a map.

* Test JSON serialization of String Enum as map key
  • Loading branch information
A. Alonso Dominguez authored and lloydmeta committed Mar 14, 2018
1 parent d328c23 commit fea39d6
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 4 deletions.
11 changes: 11 additions & 0 deletions enumeratum-circe/src/main/scala/enumeratum/values/Circe.scala
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,15 @@ object Circe {
}
}
}

def keyEncoder[EntryType <: ValueEnumEntry[String]](
enum: ValueEnum[String, EntryType]
): KeyEncoder[EntryType] =
KeyEncoder.instance(_.value)

def keyDecoder[EntryType <: ValueEnumEntry[String]](
enum: ValueEnum[String, EntryType]
): KeyDecoder[EntryType] =
KeyDecoder.instance(enum.withValueOpt)

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package enumeratum.values

import io.circe.{Decoder, Encoder}
import io.circe.{Decoder, Encoder, KeyDecoder, KeyEncoder}

/**
* Created by Lloyd on 4/14/16.
Expand Down Expand Up @@ -81,6 +81,9 @@ trait StringCirceEnum[EntryType <: StringEnumEntry] extends CirceValueEnum[Strin
this: ValueEnum[String, EntryType] =>
implicit val circeEncoder: Encoder[EntryType] = Circe.encoder(this)
implicit val circeDecoder: Decoder[EntryType] = Circe.decoder(this)

implicit val circeKeyEncoder: KeyEncoder[EntryType] = Circe.keyEncoder(this)
implicit val circeKeyDecoder: KeyDecoder[EntryType] = Circe.keyDecoder(this)
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package enumeratum.values

import org.scalatest.{FunSpec, Matchers}
import cats.syntax.either._
import io.circe.{Decoder, Encoder, Json}
import io.circe.{Decoder, Encoder, KeyDecoder, KeyEncoder, Json}
import io.circe.syntax._

/**
Expand All @@ -16,6 +16,7 @@ class CirceValueEnumSpec extends FunSpec with Matchers {
testCirceEnum("ShortCirceEnum", CirceDrinks)
testCirceEnum("IntCirceEnum", CirceLibraryItem)
testCirceEnum("StringCirceEnum", CirceOperatingSystem)
testCirceKeyEnum("StringCirceEnum", CirceOperatingSystem)
testCirceEnum("CharEnum", CirceAlphabet)
testCirceEnum("ByteEnum", CirceBites)
testCirceEnum("IntCirceEnum with val value members", CirceMovieGenre)
Expand Down Expand Up @@ -60,6 +61,27 @@ class CirceValueEnumSpec extends FunSpec with Matchers {
}
}

private def testCirceKeyEnum[EntryType <: ValueEnumEntry[String]: KeyEncoder: KeyDecoder](
enumKind: String,
enum: ValueEnum[String, EntryType] with CirceValueEnum[String, EntryType]
): Unit = {
describe(s"$enumKind as Key") {
describe("to JSON") {
it("should work") {
val map = enum.values.toStream.zip(Stream.from(1)).toMap
map.asJson.as[Map[EntryType, Int]] shouldBe Right(map)
}
}

describe("from JSON") {
it("should fail to parse random JSON into a map") {
val invalidJsonMap = Stream.from(1).map(_.toString).take(10).toStream.zip(Stream.from(1)).toMap.asJson
invalidJsonMap.as[Map[EntryType, Int]].isLeft shouldBe true
}
}
}
}

}

sealed abstract class CirceContentType(val value: Long, name: String) extends LongEnumEntry
Expand Down
4 changes: 2 additions & 2 deletions project/plugins.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ resolvers ++= Seq(
Classpaths.sbtPluginReleases
)

lazy val neoScalafmtVersion = "1.14"
lazy val neoScalafmtVersion = "1.15"

addSbtPlugin("com.lucidchart" % "sbt-scalafmt" % neoScalafmtVersion)

Expand All @@ -24,4 +24,4 @@ addSbtPlugin("org.wartremover" % "sbt-wartremover" % "2.2.0")

addSbtPlugin("com.github.tkawachi" % "sbt-doctest" % "0.7.1")

addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.1.0")
addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.1.0")

0 comments on commit fea39d6

Please sign in to comment.