Skip to content

Commit

Permalink
Add circe enumeration convenience methods
Browse files Browse the repository at this point in the history
  • Loading branch information
taig committed Jun 23, 2023
1 parent bf64d0e commit cf641e4
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 5 deletions.
20 changes: 18 additions & 2 deletions modules/circe/src/main/scala/io/taig/enumeration/ext/circe.scala
Original file line number Diff line number Diff line change
@@ -1,17 +1,33 @@
package io.taig.enumeration.ext

import cats.Show
import cats.{Hash, Show}
import cats.syntax.all.*
import io.circe.{Decoder, Encoder}
import io.circe.{Codec, Decoder, Encoder}

trait circe:

implicit def decodeMapping[A, B](using mapping: Mapping[A, B], decoder: Decoder[B])(using Show[B]): Decoder[A] =
decoder.emap: b =>
mapping
.prj(b)
.toRight(s"Couldn't decode value '$b.' Allowed values: '${mapping.values.map(mapping.inj).mkString(",")}'")

def decoderEnumeration[A, B: Show: Hash](
f: A => B
)(using EnumerationValues.Aux[A, A])(using decoder: Decoder[B]): Decoder[A] =
decodeMapping(using Mapping.enumeration(f), decoder)

implicit def encodeMapping[A, B](using mapping: Mapping[A, B], encoder: Encoder[B]): Encoder[A] =
encoder.contramap(mapping.inj)

def encoderEnumeration[A, B: Hash](
f: A => B
)(using EnumerationValues.Aux[A, A])(using encoder: Encoder[B]): Encoder[A] =
encodeMapping(using Mapping.enumeration(f), encoder)

def codecEnumeration[A, B: Show: Hash](
f: A => B
)(using EnumerationValues.Aux[A, A], Decoder[B], Encoder[B]): Codec[A] =
Codec.from(decoderEnumeration(f), encoderEnumeration(f))

object circe extends circe
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,49 @@ final class CirceTest extends FunSuite:
case Dog

object Animal:
given Mapping[Animal, String] = Mapping.enumeration:
val mapping: Animal => String =
case Animal.Bird => "bird"
case Animal.Cat => "cat"
case Animal.Dog => "dog"

test("decode"):
given Mapping[Animal, String] = Mapping.enumeration(mapping)

test("decodeMapping"):
assertEquals(obtained = Decoder[Animal].decodeJson(Json.fromString("dog")), expected = Right(Animal.Dog))
assertEquals(
obtained = Decoder[Animal].decodeJson(Json.fromString("whale")),
expected = Left(DecodingFailure("Couldn't decode value 'whale.' Allowed values: 'bird,cat,dog'", List.empty))
)

test("encode"):
test("decoderEnumeration"):
assertEquals(
obtained = decoderEnumeration(Animal.mapping).decodeJson(Json.fromString("dog")),
expected = Right(Animal.Dog)
)
assertEquals(
obtained = decoderEnumeration(Animal.mapping).decodeJson(Json.fromString("whale")),
expected = Left(DecodingFailure("Couldn't decode value 'whale.' Allowed values: 'bird,cat,dog'", List.empty))
)

test("encodeMapping"):
assertEquals(obtained = Encoder[Animal].apply(Animal.Dog), expected = Json.fromString("dog"))

test("encoderEnumeration"):
assertEquals(
obtained = encoderEnumeration(Animal.mapping).apply(Animal.Dog),
expected = Json.fromString("dog")
)

test("codecEnumeration"):
assertEquals(
obtained = codecEnumeration(Animal.mapping).decodeJson(Json.fromString("dog")),
expected = Right(Animal.Dog)
)
assertEquals(
obtained = codecEnumeration(Animal.mapping).decodeJson(Json.fromString("whale")),
expected = Left(DecodingFailure("Couldn't decode value 'whale.' Allowed values: 'bird,cat,dog'", List.empty))
)
assertEquals(
obtained = codecEnumeration(Animal.mapping).apply(Animal.Dog),
expected = Json.fromString("dog")
)

0 comments on commit cf641e4

Please sign in to comment.