Skip to content

Instantly share code, notes, and snippets.

@stsatlantis
Created April 12, 2019 12:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stsatlantis/0ceef15f46136989cc351838f6c5fced to your computer and use it in GitHub Desktop.
Save stsatlantis/0ceef15f46136989cc351838f6c5fced to your computer and use it in GitHub Desktop.
Circe Parent encoder
package object parent {
final def deriveEncoderWithParentFields[A](implicit encode: Lazy[ParentObjectEncoder[A]]): ObjectEncoder[A] = encode.value
}
import shapeless.HList
trait ParentEncoder[-T] {
type Repr <: HList
def to(t: T): Repr
}
object ParentEncoder {
type Aux[T, Repr0] = ParentEncoder[T] {type Repr = Repr0}
}
import io.circe.{JsonObject, ObjectEncoder}
import io.circe.generic.encoding.ReprObjectEncoder
import shapeless.{HList, LabelledGeneric, Lazy}
import shapeless.ops.record.{Merger, Remover}
abstract class ParentObjectEncoder[A] extends ObjectEncoder[A]
object ParentObjectEncoder {
implicit def materializeProduct[
P,
K,
EL <: HList,
T <: P,
V <: HList,
RO <: HList](
implicit
gen: LabelledGeneric.Aux[T, V],
evGen: ParentEncoder.Aux[P, EL],
merger: Merger.Aux[EL, V, RO],
remover: Remover.Aux[RO, K, V]
): LabelledGeneric.Aux[T, RO] =
new LabelledGeneric[T] {
type Repr = RO
def to(t: T): Repr = merger(evGen.to(t), gen.to(t))
override def from(r: RO): T = gen.from(remover(r))
}
implicit def deriveEncoder[A <: P, R <: HList, P, PR <: HList, Out <: HList](implicit
gen: LabelledGeneric.Aux[A, R],
pgen: ParentEncoder.Aux[P, PR],
merger: Merger.Aux[PR, R, Out],
encode: Lazy[ReprObjectEncoder[Out]]
): ParentObjectEncoder[A] = new ParentObjectEncoder[A] {
final def encodeObject(a: A): JsonObject = encode.value.encodeObject(merger(pgen.to(a), gen.to(a)))
}
}
import io.circe.{Decoder, Encoder}
import io.circe.generic.semiauto._
import io.inrab.circe.generic.extras.parent._
import shapeless._
import labelled.FieldType
import syntax.singleton._
import tag.@@
sealed abstract class Event(override val `type`: String) extends Evnt
sealed trait Evnt extends Product with Serializable {
val `type`: String
}
final case class NameEvent(name: String) extends Event("name")
implicit val eventLGen = new ParentEncoder[Event] {
override type Repr = FieldType[Symbol @@ Witness.`"type"`.T, String] :: HNil
override def to(t: Event): Repr = ('type ->> t.`type`) :: HNil
}
implicit val nameEventEncoder: Encoder[NameEvent] = deriveEncoderWithParentFields[NameEvent]
implicit val nameEventDecoder: Decoder[NameEvent] = deriveDecoder[NameEvent]
val name = "Bobby"
val bobby = NameEvent(name)
val bobbyJson = Json.obj(
"name" -> Json.fromString(name),
"type" -> Json.fromString("name")
)
bobby.asJson shouldBe bobbyJson
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment