Created
August 17, 2019 11:23
-
-
Save zsolt-donca/74aa66a49aaa30df5509e4d96bfa8001 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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