Skip to content

Instantly share code, notes, and snippets.

@eirirlar
Last active November 15, 2016 23:06
Show Gist options
  • Save eirirlar/efd3d06ab1b9fd923918f1a733421b14 to your computer and use it in GitHub Desktop.
Save eirirlar/efd3d06ab1b9fd923918f1a733421b14 to your computer and use it in GitHub Desktop.
Patch and circe decoder
package com.kodeworks.clusterfuck.util
import language.experimental.macros
import language.dynamics
import cats.syntax.option._
import io.circe._
import shapeless._
import shapeless.labelled._
import shapeless.ops.hlist.{Intersection, Length, Mapped, Mapper, RemoveAll, Zip, ZipWithKeys}
import shapeless.ops.record.{SelectAll, UnzipFields, Values}
import shapeless.tag.{@@, Tagger}
object patch {
type PatchCoprodType[P] = P :+: Patch[P] :+: String :+: None.type :+: CNil
sealed trait Patch[Patchable] extends (Patchable => Patchable) {
type OptionalFields <: HList
def updates: OptionalFields
def apply(patchable: Patchable): Patchable
}
object dec {
implicit def decodePatchable[
Patchable,
PatchableFields <: HList,
PatchableKeys <: HList,
PatchableValues <: HList,
PatchableValuesCoprod <: HList,
PatchableCoprod <: HList,
ZipPatchableCoprod <: HList
]
(implicit
patchableLG: LabelledGeneric.Aux[Patchable, PatchableFields]
, patchableUnzip: UnzipFields.Aux[PatchableFields, PatchableKeys, PatchableValues]
, patchableValuesCoprod: Mapped.Aux[PatchableValues, PatchCoprodType, PatchableValuesCoprod]
, patchableCoprod: ZipWithKeys.Aux[PatchableKeys, PatchableValuesCoprod, PatchableCoprod]
, patchableCoprodDecoder: Lazy[Decoder[PatchableCoprod]]
, patchableValuesCoprod0: Values.Aux[PatchableCoprod, PatchableValuesCoprod]
, zipPatchableCoprod: Zip.Aux[PatchableValues :: PatchableValuesCoprod :: HNil, ZipPatchableCoprod]
, mapZipPatchableCoprod: Mapper.Aux[patchApplyPatchable.type, ZipPatchableCoprod, PatchableValues]
, patchableZipWithKeys: ZipWithKeys.Aux[PatchableKeys, PatchableValues, PatchableFields]
)
: Decoder[Patch[Patchable]] =
patchableCoprodDecoder.value.map {
patchCoprod => new Patch[Patchable] {
type OptionalFields = PatchableCoprod
val updates: OptionalFields = patchCoprod
def apply(patchable: Patchable) = {
val patchableFields0: PatchableFields = patchableLG.to(patchable)
val patchableValues0 = patchableUnzip.values(patchableFields0)
val patchableValuesCoprod1 = patchableValuesCoprod0(patchCoprod)
val zipPatchableCoprod0 = zipPatchableCoprod(patchableValues0 :: patchableValuesCoprod1 :: HNil)
val mapZipPatchableCoprod0 = mapZipPatchableCoprod(zipPatchableCoprod0)
val patchableZipWithKeys0 = patchableZipWithKeys(mapZipPatchableCoprod0)
val patchable0 = patchableLG.from(patchableZipWithKeys0)
patchable0
}
}
}
implicit def decodeCoprod[Patchable]
(implicit pd: Decoder[Patchable]
, ppd: Lazy[Decoder[Patch[Patchable]]] = null
)
: Decoder[PatchCoprodType[Patchable]] = {
import Decoder._
withReattempt(c =>
if (c.succeeded) {
if (c.any.focus.isNull) Right(Inr(Inr(Inr(Inl(None)))))
else pd(c.any) match {
case Right(a) => Right(Inl(a))
case Left(df) if df.history.isEmpty => Right(Inr(Inr(Inr(Inl(None)))))
case Left(df) =>
if (c.any.focus.isObject && null != ppd) {
ppd.value(c.any) match {
case Right(a) => Right(Inr(Inl(a)))
case Left(df) => Left(df)
}
} else
Decoder[String].apply(c.any) match {
case Right(a) => Right(Inr(Inr(Inl(a))))
case Left(df) => Left(df)
}
}
} else if (!c.history.takeWhile(_.failed).exists(_.incorrectFocus)) Right(Inr(Inr(Inr(Inl(None)))))
else {
Left(DecodingFailure("[A]PatchCoprodType[A]", c.history))
}
)
}
}
object Patch {
type Aux[Patchable, OptionalFields0 <: HList] = Patch[Patchable] {type OptionalFields = OptionalFields0}
object mergePatchPatchable extends Poly1 {
implicit def apply[Key <: Symbol, Value] = at[(FieldType[Key, Option[Value]], FieldType[Key, Value])] {
case (patch, patchable) => field[Key](patch.getOrElse(patchable))
}
}
def apply[Patchable](patchable: Patchable): Patch[Patchable] = new Patch[Patchable] {
override type OptionalFields = Patchable :: HNil
override def updates: OptionalFields = patchable :: HNil
override def apply(p: Patchable): Patchable = patchable
}
def apply[T] = new MkPatch[T]
class MkPatch[Patchable] extends Dynamic {
def applyRecord[
PatchableFields <: HList,
PatchableKeys <: HList,
PatchableValues <: HList,
PatchableValuesCoprod <: HList,
PatchableCoprod <: HList,
Patch0 <: HList,
PatchKeys <: HList,
PatchValues <: HList,
PatchValueTypes <: HList,
PatchValuesTagged <: HList,
PatchValuesCoprod <: HList,
PatchCoprod <: HList,
NotPatch <: HList,
NotPatchKeys <: HList,
NotPatchValues <: HList,
NotPatchValuesCoprod <: HList,
NotPatchCoprod <: HList,
ZipPatchableCoprod <: HList,
KeysIntersect <: HList,
KeysIntersectLength <: Nat
](patch: Patch0)
(implicit
patchableLG: LabelledGeneric.Aux[Patchable, PatchableFields]
, patchableUnzip: UnzipFields.Aux[PatchableFields, PatchableKeys, PatchableValues]
, patchableValuesCoprod: Mapped.Aux[PatchableValues, PatchCoprodType, PatchableValuesCoprod]
, patchableCoprod: ZipWithKeys.Aux[PatchableKeys, PatchableValuesCoprod, PatchableCoprod]
, patchableValuesCoprod0: Values.Aux[PatchableCoprod, PatchableValuesCoprod]
, patchUnzip: UnzipFields.Aux[Patch0, PatchKeys, PatchValues]
, keysIntersect: Intersection.Aux[PatchKeys, PatchableKeys, KeysIntersect]
, keysIntersectLength: Length.Aux[KeysIntersect, KeysIntersectLength]
, keysLength: Length.Aux[Patch0, KeysIntersectLength]
, patchKeysRemoveAll: RemoveAll.Aux[PatchableKeys, PatchKeys, (PatchKeys, NotPatchKeys)]
, patchValueTypes: SelectAll.Aux[PatchableFields, PatchKeys, PatchValueTypes]
, patchValuesTagged: Taggeds.Aux[PatchValueTypes, PatchValues, PatchValuesTagged]
, patchValuesCoprod: Mapper.Aux[coprod.type, PatchValuesTagged, PatchValuesCoprod]
, patchCoprod: ZipWithKeys.Aux[PatchKeys, PatchValuesCoprod, PatchCoprod]
, notPatchValuesCoprod: SelectAll.Aux[PatchableCoprod, NotPatchKeys, NotPatchValuesCoprod]
, notPatchNones: Nones[NotPatchValuesCoprod]
, notPatchCoprod: ZipWithKeys.Aux[NotPatchKeys, NotPatchValuesCoprod, NotPatchCoprod]
, buildPatchable: RemoveAll.Aux[PatchableCoprod, PatchCoprod, (PatchCoprod, NotPatchCoprod)]
, zipPatchableCoprod: Zip.Aux[PatchableValues :: PatchableValuesCoprod :: HNil, ZipPatchableCoprod]
, mapZipPatchableCoprod: Mapper.Aux[patchApplyPatchable.type, ZipPatchableCoprod, PatchableValues]
, patchableZipWithKeys: ZipWithKeys.Aux[PatchableKeys, PatchableValues, PatchableFields]
): Patch[Patchable] =
new Patch[Patchable] {
val patchValues = patchUnzip.values(patch)
val patchValuesTagged0 = patchValuesTagged(patchValues)
val patchValuesCoprod0 = patchValuesCoprod(patchValuesTagged0)
val patchCoprod0 = patchCoprod(patchValuesCoprod0)
val patchableKeys = patchableUnzip.keys()
val patchRemoveAll0 = patchKeysRemoveAll(patchableKeys)
val notPatchKeys = patchRemoveAll0._2
val notPatchCoprod0 = notPatchCoprod(notPatchNones())
val buildPatchable0 = buildPatchable.reinsert(patchCoprod0, notPatchCoprod0)
override type OptionalFields = PatchableCoprod
override def updates: OptionalFields = buildPatchable0
override def apply(patchable: Patchable): Patchable = {
val patchableFields0 = patchableLG.to(patchable)
val patchableValues0 = patchableUnzip.values(patchableFields0)
val patchableValuesCoprod1 = patchableValuesCoprod0(buildPatchable0)
val zipPatchableCoprod0 = zipPatchableCoprod(patchableValues0 :: patchableValuesCoprod1 :: HNil)
val mapZipPatchableCoprod0 = mapZipPatchableCoprod(zipPatchableCoprod0)
val patchableZipWithKeys0 = patchableZipWithKeys(mapZipPatchableCoprod0)
val patchable0 = patchableLG.from(patchableZipWithKeys0)
patchable0
}
}
def applyDynamicNamed(method: String)(rec: Any*): Patch[Patchable] = macro RecordMacros.forwardNamedImpl
}
}
object patchApplyPatchable extends Poly1 {
implicit def default[P] =
at[(P, PatchCoprodType[P])] {
case (_, Inl(value)) =>
value
case (p, Inr(Inl(patch))) =>
patch(p)
case (original, _) =>
original
}
}
object toOption extends Poly1 {
implicit def apply[P] = at[P](_.some)
}
def taggeds[
Tags <: HList,
Taggables <: HList
]
(implicit
taggeds: Taggeds[Tags, Taggables]
): taggeds.type = taggeds
sealed trait Taggeds[
Tags <: HList,
Taggables <: HList
] {
type Taggeds <: HList
def apply(taggables: Taggables): Taggeds
}
object Taggeds {
final type Aux[
Tags <: HList,
Taggables0 <: HList,
Taggeds0 <: HList
] = Taggeds[Tags, Taggables0] {type Taggeds = Taggeds0}
}
implicit def taggeds0[
Tags <: HList,
Taggers0 <: HList,
Taggables0 <: HList,
Taggeds0 <: HList
]
(implicit
asTaggers: Mapped.Aux[Tags, Tagger, Taggers0],
taggers: Taggers[Taggers0],
tagApply: TagApply.Aux[Taggers0, Taggables0, Taggeds0]
)
: Taggeds.Aux[Tags, Taggables0, Taggeds0] = new Taggeds[Tags, Taggables0] {
override type Taggeds = Taggeds0
override def apply(taggables: Taggables0): Taggeds0 =
tagApply(taggers(), taggables)
}
trait TagApply[FL <: HList, AL <: HList] extends DepFn2[FL, AL] with Serializable {
type Out <: HList
}
object TagApply {
def apply[
TaggerList <: HList,
TaggableList <: HList
]
(implicit
tagApply: TagApply[TaggerList, TaggableList]
): tagApply.type =
tagApply
type Aux[FL <: HList, AL <: HList, Out0 <: HList] = TagApply[FL, AL] {type Out = Out0}
implicit def hnilTagApply: TagApply.Aux[HNil, HNil, HNil] =
new TagApply[HNil, HNil] {
type Out = HNil
def apply(fl: HNil, al: HNil): Out = HNil
}
implicit def hconsTagApply[
Tag0,
Taggable,
Tags <: HList,
Taggables <: HList
]
(implicit
tagApply: TagApply[Tags, Taggables]
): TagApply.Aux[Tagger[Tag0] :: Tags, Taggable :: Taggables, (Taggable @@ Tag0) :: tagApply.Out] =
new TagApply[Tagger[Tag0] :: Tags, Taggable :: Taggables] {
type Out = (Taggable @@ Tag0) :: tagApply.Out
def apply(fl: Tagger[Tag0] :: Tags, al: Taggable :: Taggables): Out = fl.head(al.head) :: tagApply(fl.tail, al.tail)
}
}
final class Nones[Noneable <: HList](noneable: Noneable) {
def apply(): Noneable = noneable
}
object Nones {
def apply[Noneable <: HList](implicit nones: Nones[Noneable]): Nones[Noneable] = nones
implicit def nonesHNil: Nones[HNil] = new Nones(HNil)
implicit def nonesHCons[H, T <: HList](implicit t: Nones[T]): Nones[PatchCoprodType[H] :: T] =
new Nones(Inr(Inr(Inr(Inl(None)))) :: t())
}
final class Taggers[Tags <: HList](tags: Tags) {
def apply(): Tags = tags
}
object Taggers {
def apply[Tags <: HList](implicit taggers: Taggers[Tags]): Taggers[Tags] = taggers
implicit def taggersHNil: Taggers[HNil] = new Taggers(HNil)
implicit def taggersHCons[H, T <: HList](implicit t: Taggers[T]): Taggers[Tagger[H] :: T] =
new Taggers(new Tagger[H] :: t())
}
trait coprodLP {
this: Poly1 =>
implicit def s[P]: Case.Aux[String @@ P, PatchCoprodType[P]] =
at[String @@ P] { s =>
Inr(Inr(Inl(s)))
}
}
object coprod extends Poly1 with coprodLP {
implicit def p[P]: Case.Aux[P @@ P, PatchCoprodType[P]] =
at[P @@ P] { p =>
Inl(p)
}
implicit def pp[P]: Case.Aux[Patch[P] @@ P, PatchCoprodType[P]] =
at[Patch[P] @@ P] { pp =>
Inr(Inl(pp))
}
}
}
import patch._
import dec._
import io.circe._
import parser._
import generic.auto._
import shapes._
case class Pond(
name: String,
depth: Int = 4,
color: Double = 3.2
)
case class Duck(
name: String,
pond: Pond,
age: Int = 0,
walkingStyle: Int = -1,
canQuack: Boolean = false,
canFly: Boolean = false
)
object Testy extends App {
//create programatically
val patch0 = Patch[Duck].apply(
name = "donald",
pond = Patch[Pond].apply(
name = "ponder",
depth = 41,
color = "red" //wrong type
)
)
//--
//parse from json
val d = Duck("mrquackyer", Pond("deep pond"))
val json = parse("""{"name":"donald","pond":{"name":"ponder","depth":41,"color":"red"}}""").right.get
val i = Decoder[Patch[Duck]]
val patch = i.decodeJson(json).right.get
println("patch: " + patch.updates)
val upd = patch(d)
println("upd: " + upd)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment