Skip to content

Instantly share code, notes, and snippets.

@zsolt-donca
Created August 25, 2019 14:45
Show Gist options
  • Save zsolt-donca/a009e219978a50b202cf45e495631bc4 to your computer and use it in GitHub Desktop.
Save zsolt-donca/a009e219978a50b202cf45e495631bc4 to your computer and use it in GitHub Desktop.
EnumCodec.scala - working
package testing
import shapeless.labelled.FieldType
import shapeless.{:+:, CNil, Coproduct, HNil, Inl, Inr, LabelledGeneric, Lazy, Witness, labelled}
trait EnumCodec[T] {
def encode: T => String
def decode: String => Option[T]
}
object EnumCodec {
def apply[T](implicit instance: EnumCodec[T]): EnumCodec[T] = instance
implicit def genInst[T, R](implicit gen: LabelledGeneric.Aux[T, R], genEnumCodec: EnumCodec[R]): EnumCodec[T] = new EnumCodec[T] {
override def encode: T => String = value => genEnumCodec.encode(gen.to(value))
override def decode: String => Option[T] = genEnumCodec.decode andThen (_.map(gen.from))
}
implicit def sumEnumCodec[K <: Symbol, H, T <: Coproduct](implicit
witness: Witness.Aux[K],
hEncoder: Lazy[EnumCase[H]],
tEncoder: EnumCodec[T]
): EnumCodec[FieldType[K, H] :+: T] =
new EnumCodec[FieldType[K, H] :+: T] {
override def encode: FieldType[K, H] :+: T => String = {
case Inl(_) => witness.value.name
case Inr(tail) => tEncoder.encode(tail)
}
override def decode: String => Option[FieldType[K, H] :+: T] = value => {
if (value == witness.value.name) {
val value = hEncoder.value.value
Some(Inl(labelled.field[K](value)))
} else {
tEncoder.decode(value).map(Inr(_))
}
}
}
implicit def cnilEnumCodec: EnumCodec[CNil] = new EnumCodec[CNil] {
override def encode: CNil => String = sys.error("Impossible")
override def decode: String => Option[CNil] = _ => None
}
}
trait EnumCase[T] {
def value: T
}
object EnumCase {
implicit def hNilCase: EnumCase[HNil] = new EnumCase[HNil] {
override def value: HNil = HNil
}
implicit def caseObjectEnumCase[T, R](implicit gen: LabelledGeneric.Aux[T, R], enumCase: EnumCase[R]): EnumCase[T] = new EnumCase[T] {
override def value: T = gen.from(enumCase.value)
}
}
sealed trait TestEnum
case object CaseOne extends TestEnum
object CirceEnumCodecTest extends App {
val enumCodec: EnumCodec[TestEnum] = EnumCodec[TestEnum]
assert(enumCodec.encode(CaseOne) == "CaseOne")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment