Skip to content

Instantly share code, notes, and snippets.

@zsolt-donca
Created August 17, 2019 11:23
Show Gist options
  • Save zsolt-donca/74aa66a49aaa30df5509e4d96bfa8001 to your computer and use it in GitHub Desktop.
Save zsolt-donca/74aa66a49aaa30df5509e4d96bfa8001 to your computer and use it in GitHub Desktop.
package testing
import org.scalatest.FunSuite
import shapeless.labelled.FieldType
import shapeless.{:+:, CNil, Coproduct, Generic, HNil, Inl, Inr, LabelledGeneric, Lazy, Witness}
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[FieldType[K, 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) {
Some(Inl(hEncoder.value.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: Generic.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
class CirceEnumCodecTest extends FunSuite {
test("Simple case") {
// below line doesn't compile
// Error:(68, 51) could not find implicit value for parameter instance: testing.EnumCodec[testing.TestEnum]
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